pervert-monkey 1.0.9 → 1.0.10

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 (65) hide show
  1. package/dist/core/pervertmonkey.core.es.d.ts +121 -59
  2. package/dist/core/pervertmonkey.core.es.js +1001 -315
  3. package/dist/core/pervertmonkey.core.es.js.map +1 -1
  4. package/dist/core/pervertmonkey.core.umd.js +1001 -315
  5. package/dist/core/pervertmonkey.core.umd.js.map +1 -1
  6. package/dist/userscripts/3hentai.user.js +14 -8
  7. package/dist/userscripts/camgirlfinder.user.js +1 -1
  8. package/dist/userscripts/camwhores.user.js +15 -9
  9. package/dist/userscripts/e-hentai.user.js +12 -6
  10. package/dist/userscripts/ebalka.user.js +11 -5
  11. package/dist/userscripts/eporner.user.js +15 -10
  12. package/dist/userscripts/erome.user.js +11 -10
  13. package/dist/userscripts/eroprofile.user.js +9 -5
  14. package/dist/userscripts/javhdporn.user.js +9 -5
  15. package/dist/userscripts/missav.user.js +10 -6
  16. package/dist/userscripts/motherless.user.js +11 -7
  17. package/dist/userscripts/namethatporn.user.js +17 -13
  18. package/dist/userscripts/nhentai.user.js +12 -6
  19. package/dist/userscripts/obmenvsem.user.js +14 -18
  20. package/dist/userscripts/pornhub.user.js +14 -10
  21. package/dist/userscripts/spankbang.user.js +10 -6
  22. package/dist/userscripts/thisvid.user.js +27 -21
  23. package/dist/userscripts/xhamster.user.js +26 -26
  24. package/dist/userscripts/xvideos.user.js +19 -17
  25. package/package.json +5 -3
  26. package/src/core/{data-control → data-handler}/data-filter.ts +3 -8
  27. package/src/core/{data-control → data-handler}/data-manager.ts +8 -7
  28. package/src/core/data-handler/index.ts +2 -0
  29. package/src/core/index.ts +4 -4
  30. package/src/core/infinite-scroll/index.ts +20 -29
  31. package/src/core/parsers/index.ts +4 -0
  32. package/src/core/{pagination-parsing → parsers/pagination-parser}/index.ts +3 -0
  33. package/src/core/{pagination-parsing → parsers/pagination-parser}/pagination-strategies/PaginationStrategy.ts +1 -1
  34. package/src/core/{pagination-parsing → parsers/pagination-parser}/pagination-strategies/PaginationStrategyDataParams.ts +1 -1
  35. package/src/core/{pagination-parsing → parsers/pagination-parser}/pagination-strategies/PaginationStrategyPathnameParams.ts +2 -1
  36. package/src/core/{pagination-parsing → parsers/pagination-parser}/pagination-utils/index.ts +1 -4
  37. package/src/core/parsers/thumb-data-parser.ts +96 -0
  38. package/src/core/parsers/thumb-img-parser.ts +62 -0
  39. package/src/core/parsers/thumbs-parser.ts +29 -0
  40. package/src/core/rules/index.ts +39 -207
  41. package/src/userscripts/index.ts +1 -1
  42. package/src/userscripts/scripts/3hentai.ts +14 -6
  43. package/src/userscripts/scripts/camwhores.ts +19 -13
  44. package/src/userscripts/scripts/e-hentai.ts +13 -7
  45. package/src/userscripts/scripts/ebalka.ts +11 -5
  46. package/src/userscripts/scripts/eporner.ts +16 -10
  47. package/src/userscripts/scripts/erome.ts +11 -10
  48. package/src/userscripts/scripts/eroprofile.ts +10 -6
  49. package/src/userscripts/scripts/javhdporn.ts +9 -5
  50. package/src/userscripts/scripts/missav.ts +10 -6
  51. package/src/userscripts/scripts/motherless.ts +11 -7
  52. package/src/userscripts/scripts/namethatporn.ts +18 -14
  53. package/src/userscripts/scripts/nhentai.ts +13 -8
  54. package/src/userscripts/scripts/obmenvsem.ts +14 -13
  55. package/src/userscripts/scripts/pornhub.ts +14 -10
  56. package/src/userscripts/scripts/spankbang.ts +10 -6
  57. package/src/userscripts/scripts/thisvid.ts +32 -27
  58. package/src/userscripts/scripts/xhamster.ts +37 -38
  59. package/src/userscripts/scripts/xvideos.ts +18 -16
  60. package/src/utils/index.ts +7 -19
  61. package/src/utils/parsers/index.ts +4 -0
  62. package/src/utils/strings/index.ts +2 -0
  63. package/src/core/data-control/index.ts +0 -2
  64. /package/src/core/{pagination-parsing → parsers/pagination-parser}/pagination-strategies/PaginationStrategySearchParams.ts +0 -0
  65. /package/src/core/{pagination-parsing → parsers/pagination-parser}/pagination-strategies/index.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  import type { MonkeyUserScript } from 'vite-plugin-monkey';
2
2
  import { GM_addStyle, unsafeWindow } from '$';
3
- import { RulesGlobal } from '../../core';
3
+ import { Rules } from '../../core';
4
4
 
5
5
  export const meta: MonkeyUserScript = {
6
6
  name: 'Erome PervertMonkey',
@@ -12,16 +12,17 @@ export const meta: MonkeyUserScript = {
12
12
  const $ = (unsafeWindow as any).$;
13
13
  declare var LazyLoad: ObjectConstructor;
14
14
 
15
- const rules = new RulesGlobal({
15
+ const rules = new Rules({
16
16
  containerSelector: '#albums',
17
- thumbsSelector: 'div[id^=album-]',
18
- titleSelector: '.album-title',
19
- uploaderSelector: '.album-user',
20
17
  gropeStrategy: 'all-in-one',
21
- customThumbDataSelectors: {
22
- videoAlbum: {
23
- type: 'boolean',
24
- selector: '.album-videos',
18
+ thumbs: {
19
+ selector: 'div[id^=album-]',
20
+ },
21
+ thumb: {
22
+ selectors: {
23
+ title: '.album-title',
24
+ uploader: '.album-user',
25
+ videoAlbum: { type: 'boolean', selector: '.album-videos' },
25
26
  },
26
27
  },
27
28
  storeOptions: { showPhotos: true },
@@ -57,7 +58,7 @@ const rules = new RulesGlobal({
57
58
  ],
58
59
  });
59
60
 
60
- rules.infiniteScroller?.onScroll(() => {
61
+ rules.infiniteScroller?.subject.subscribe(() => {
61
62
  setTimeout(() => new LazyLoad(), 100);
62
63
  });
63
64
 
@@ -1,5 +1,5 @@
1
1
  import type { MonkeyUserScript } from 'vite-plugin-monkey';
2
- import { RulesGlobal } from '../../core';
2
+ import { Rules } from '../../core';
3
3
 
4
4
  export const meta: MonkeyUserScript = {
5
5
  name: 'Eroprofile PervertMonkey',
@@ -10,15 +10,19 @@ export const meta: MonkeyUserScript = {
10
10
 
11
11
  document.querySelector('.videoGrid')?.after(document.querySelector('.clB') as HTMLElement);
12
12
 
13
- const rules = new RulesGlobal({
13
+ const rules = new Rules({
14
14
  paginationStrategyOptions: {
15
15
  paginationSelector: '.boxNav2',
16
16
  searchParamSelector: 'pnum',
17
17
  },
18
- titleSelector: '[title]',
19
- durationSelector: '.videoDur',
18
+ thumbs: { selector: '.video' },
19
+ thumb: {
20
+ selectors: {
21
+ title: '[title]',
22
+ duration: '.videoDur',
23
+ }
24
+ },
20
25
  containerSelector: '.videoGrid',
21
- thumbsSelector: '.video',
22
26
  customDataSelectorFns: ['filterInclude', 'filterExclude', 'filterDuration'],
23
27
  schemeOptions: [
24
28
  'Text Filter',
@@ -27,7 +31,7 @@ const rules = new RulesGlobal({
27
31
  title: 'Sort By ',
28
32
  content: [
29
33
  {
30
- 'sort by duration': () => {},
34
+ 'sort by duration': () => { },
31
35
  },
32
36
  ],
33
37
  },
@@ -1,5 +1,5 @@
1
1
  import type { MonkeyUserScript } from 'vite-plugin-monkey';
2
- import { RulesGlobal } from '../../core';
2
+ import { Rules } from '../../core';
3
3
 
4
4
  export const meta: MonkeyUserScript = {
5
5
  name: 'Javhdporn PervertMonkey',
@@ -11,11 +11,15 @@ export const meta: MonkeyUserScript = {
11
11
  ],
12
12
  };
13
13
 
14
- const rules = new RulesGlobal({
14
+ const rules = new Rules({
15
15
  containerSelector: 'div:has(> article)',
16
- thumbsSelector: 'article.thumb-block',
17
- titleSelector: 'header.entry-header',
18
- durationSelector: '.duration',
16
+ thumbs: { selector: 'article.thumb-block' },
17
+ thumb: {
18
+ selectors: {
19
+ title: 'header.entry-header',
20
+ duration: '.duration',
21
+ }
22
+ },
19
23
  paginationStrategyOptions: {
20
24
  pathnameSelector: /\/page\/(\d+)\/?$/,
21
25
  },
@@ -1,5 +1,5 @@
1
1
  import type { MonkeyUserScript } from 'vite-plugin-monkey';
2
- import { RulesGlobal } from '../../core';
2
+ import { Rules } from '../../core';
3
3
 
4
4
  export const meta: MonkeyUserScript = {
5
5
  name: 'Missav PervertMonkey',
@@ -14,14 +14,18 @@ export const meta: MonkeyUserScript = {
14
14
  ],
15
15
  };
16
16
 
17
- const rules = new RulesGlobal({
17
+ const rules = new Rules({
18
18
  paginationStrategyOptions: {
19
19
  paginationSelector: 'nav[x-data]',
20
20
  },
21
21
  containerSelector: '.grid[x-data]',
22
- thumbsSelector: 'div:has(> .thumbnail.group)',
23
- getThumbImgDataStrategy: 'auto',
24
- titleSelector: 'div > div > a.text-secondary',
25
- durationSelector: 'div > a > span.text-xs',
22
+ thumbs: { selector: 'div:has(> .thumbnail.group)' },
23
+ thumb: {
24
+ selectors: {
25
+ title: 'div > div > a.text-secondary',
26
+ duration: 'div > a > span.text-xs',
27
+ }
28
+ },
29
+ thumbImg: { strategy: 'auto' },
26
30
  schemeOptions: ['Text Filter', 'Duration Filter', 'Badge', 'Advanced'],
27
31
  });
@@ -1,6 +1,6 @@
1
1
  import type { MonkeyUserScript } from 'vite-plugin-monkey';
2
2
  import { GM_addElement, GM_addStyle, unsafeWindow } from '$';
3
- import { RulesGlobal } from '../../core';
3
+ import { Rules } from '../../core';
4
4
  import { fetchWith, OnHover, replaceElementTag, Tick } from '../../utils';
5
5
 
6
6
  export const meta: MonkeyUserScript = {
@@ -14,13 +14,17 @@ export const meta: MonkeyUserScript = {
14
14
  (unsafeWindow as any).__is_premium = true;
15
15
  const $ = (unsafeWindow as any).$;
16
16
 
17
- const rules = new RulesGlobal({
17
+ const rules = new Rules({
18
18
  containerSelectorLast: '.content-inner',
19
- thumbsSelector: '.thumb-container, .mobile-thumb',
20
- uploaderSelector: '.uploader',
21
- titleSelector: '.title',
22
- durationSelector: '.size',
23
- getThumbImgDataStrategy: 'auto',
19
+ thumbs: { selector: '.thumb-container, .mobile-thumb' },
20
+ thumb: {
21
+ selectors: {
22
+ uploader: '.uploader',
23
+ title: '.title',
24
+ duration: '.size',
25
+ }
26
+ },
27
+ thumbImg: { strategy: 'auto' },
24
28
  paginationStrategyOptions: {
25
29
  paginationSelector: '.pagination_link, .ml-pagination',
26
30
  },
@@ -1,6 +1,6 @@
1
1
  import type { MonkeyUserScript } from 'vite-plugin-monkey';
2
2
  import { unsafeWindow } from '$';
3
- import { RulesGlobal } from '../../core';
3
+ import { Rules } from '../../core';
4
4
 
5
5
  export const meta: MonkeyUserScript = {
6
6
  name: 'NameThatPorn PervertMonkey',
@@ -9,24 +9,28 @@ export const meta: MonkeyUserScript = {
9
9
  match: ['https://namethatporn.com/*'],
10
10
  };
11
11
 
12
- const rules = new RulesGlobal({
13
- thumbsSelector: '.item, .nsw_r_w',
12
+ const rules = new Rules({
13
+ thumbs: { selector: '.item, .nsw_r_w' },
14
14
  containerSelector: '#items_wrapper, #nsw_r',
15
- titleSelector: '.item_title, .nsw_r_tit',
16
- uploaderSelector: '.item_answer b, .nsw_r_desc',
15
+ thumb: {
16
+ selectors: {
17
+ title: '.item_title, .nsw_r_tit',
18
+ uploader: '.item_answer b, .nsw_r_desc',
19
+ solved: {
20
+ type: 'boolean',
21
+ selector: '.item_solved, .nsw_r_slvd',
22
+ },
23
+ }
24
+ },
25
+ thumbImg: {
26
+ strategy: 'auto',
27
+ selector: (img: HTMLImageElement) =>
28
+ img.getAttribute('data-dyn')?.concat('.webp') || (img.getAttribute('src') as string),
29
+ },
17
30
  paginationStrategyOptions: {
18
31
  paginationSelector: '#smi_wrp, #nsw_p',
19
32
  },
20
- customThumbDataSelectors: {
21
- solved: {
22
- type: 'boolean',
23
- selector: '.item_solved, .nsw_r_slvd',
24
- },
25
- },
26
33
  gropeStrategy: 'all-in-all',
27
- getThumbImgDataStrategy: 'auto',
28
- getThumbImgDataAttrSelector: (img: HTMLImageElement) =>
29
- img.getAttribute('data-dyn')?.concat('.webp') || (img.getAttribute('src') as string),
30
34
  customDataSelectorFns: [
31
35
  'filterInclude',
32
36
  'filterExclude',
@@ -1,5 +1,5 @@
1
1
  import type { MonkeyUserScript } from 'vite-plugin-monkey';
2
- import { RulesGlobal } from '../../core';
2
+ import { Rules } from '../../core';
3
3
  import { parseHtml } from '../../utils';
4
4
 
5
5
  export const meta: MonkeyUserScript = {
@@ -12,12 +12,18 @@ export const meta: MonkeyUserScript = {
12
12
  const IS_TITLE_PAGE = /^\/g\/\d+/.test(location.pathname);
13
13
  const IS_SEARCH_PAGE = /^\/search\//.test(location.pathname);
14
14
 
15
- const nhentaiRules = new RulesGlobal({
16
- getThumbImgDataAttrDelete: 'auto',
17
- getThumbImgDataStrategy: 'auto',
18
- thumbsSelector: '.gallery',
15
+ const nhentaiRules = new Rules({
16
+ thumbs: { selector: '.gallery' },
17
+ thumb: {
18
+ selectors: {
19
+ title: '.caption'
20
+ }
21
+ },
22
+ thumbImg: {
23
+ strategy: 'auto',
24
+ shouldDelete: 'auto'
25
+ },
19
26
  containerSelectorLast: '.index-container, .container',
20
- titleSelector: '.caption',
21
27
  customDataSelectorFns: ['filterInclude', 'filterExclude'],
22
28
  schemeOptions: ['Text Filter', 'Badge', 'Advanced'],
23
29
  gropeStrategy: 'all-in-all',
@@ -51,8 +57,7 @@ function filtersUI() {
51
57
  const btns = parseHtml(`<div class="sort-type"></div>`);
52
58
  groupOfButtons.forEach((k) => {
53
59
  const btn = parseHtml(
54
- `<a href="#" ${
55
- state.custom[k] ? 'style="background: rgba(59, 49, 70, 1)"' : ''
60
+ `<a href="#" ${state.custom[k] ? 'style="background: rgba(59, 49, 70, 1)"' : ''
56
61
  }>${filterDescriptors[k as keyof typeof filterDescriptors].name}</a>`,
57
62
  );
58
63
  btn.addEventListener('click', (e) => {
@@ -1,8 +1,7 @@
1
1
  import type { MonkeyUserScript } from 'vite-plugin-monkey';
2
2
  import { GM_addStyle } from '$';
3
- import { RulesGlobal } from '../../core';
4
- import { parseUrl } from '../../core/pagination-parsing/pagination-utils';
5
- import { fetchHtml } from '../../utils';
3
+ import { Rules } from '../../core';
4
+ import { fetchHtml, parseUrl } from '../../utils';
6
5
 
7
6
  export const meta: MonkeyUserScript = {
8
7
  name: 'Obmensvem PervertMonkey',
@@ -26,7 +25,7 @@ function setup() {
26
25
 
27
26
  setup();
28
27
 
29
- const rules = new RulesGlobal({
28
+ const rules = new Rules({
30
29
  paginationStrategyOptions: {
31
30
  getPaginationUrlGenerator() {
32
31
  const url = parseUrl(location.href);
@@ -41,15 +40,17 @@ const rules = new RulesGlobal({
41
40
  paginationSelector: '.item.pages, .pagination',
42
41
  },
43
42
  containerSelectorLast: '.container',
44
- thumbsSelector: '.item:has(> a.block[href *= info] > img)',
45
- getThumbImgDataAttrSelector(img) {
46
- const url = (img.closest('a') as HTMLAnchorElement).href;
47
- fetchHtml(url).then(dom => {
48
- img.src = dom.querySelector<HTMLImageElement>('img[src*=attach]')?.src || img.src;
49
- })
50
- return img.src;
43
+ thumbs: { selector: '.item:has(> a.block[href *= info] > img)' },
44
+ thumb: { strategy: 'auto-text' },
45
+ thumbImg: {
46
+ strategy: 'auto',
47
+ selector: (img) => {
48
+ const url = (img.closest('a') as HTMLAnchorElement).href;
49
+ fetchHtml(url).then((dom) => {
50
+ img.src = dom.querySelector<HTMLImageElement>('img[src*=attach]')?.src || img.src;
51
+ });
52
+ return img.src;
53
+ },
51
54
  },
52
- getThumbImgDataStrategy: 'auto',
53
- getThumbDataStrategy: 'auto-text',
54
55
  schemeOptions: ['Text Filter', 'Duration Filter', 'Badge', 'Advanced'],
55
56
  });
@@ -1,5 +1,5 @@
1
1
  import type { MonkeyUserScript } from 'vite-plugin-monkey';
2
- import { RulesGlobal } from '../../core';
2
+ import { Rules } from '../../core';
3
3
 
4
4
  export const meta: MonkeyUserScript = {
5
5
  name: 'PornHub PervertMonkey',
@@ -10,7 +10,7 @@ export const meta: MonkeyUserScript = {
10
10
  exclude: 'https://*.pornhub.com/embed/*',
11
11
  };
12
12
 
13
- const rules = new RulesGlobal({
13
+ const rules = new Rules({
14
14
  paginationStrategyOptions: {
15
15
  paginationSelector: '.paginationGated',
16
16
  overwritePaginationLast: (n: number) => (n === 9 ? 999 : n),
@@ -20,15 +20,19 @@ const rules = new RulesGlobal({
20
20
  .filter((e) => e.children.length > 0 && e.checkVisibility())
21
21
  .pop() as HTMLElement,
22
22
 
23
- dataManagerOptions: {
24
- parseDataParentHomogenity: { id: true, className: true },
23
+ dataHomogenity: { id: true, className: true },
24
+ thumbs: { selector: 'li[data-video-vkey]' },
25
+ thumb: {
26
+ selectors: {
27
+ title: 'span.title',
28
+ uploader: '.usernameWrap',
29
+ duration: '.duration',
30
+ }
31
+ },
32
+ thumbImg: {
33
+ strategy: 'auto', // WHAT?
34
+ selector: ['data-mediumthumb', 'data-image'],
25
35
  },
26
- thumbsSelector: 'li[data-video-vkey]',
27
- getThumbImgDataStrategy: 'auto',
28
- getThumbImgDataAttrSelector: ['data-mediumthumb', 'data-image'],
29
- uploaderSelector: '.usernameWrap',
30
- titleSelector: 'span.title',
31
- durationSelector: '.duration',
32
36
  gropeStrategy: 'all-in-all',
33
37
  schemeOptions: ['Text Filter', 'Duration Filter', 'Badge', 'Advanced'],
34
38
  });
@@ -1,5 +1,5 @@
1
1
  import type { MonkeyUserScript } from 'vite-plugin-monkey';
2
- import { RulesGlobal } from '../../core';
2
+ import { Rules } from '../../core';
3
3
  import { exterminateVideo, OnHover, parseHtml } from '../../utils';
4
4
 
5
5
  export const meta: MonkeyUserScript = {
@@ -9,15 +9,19 @@ export const meta: MonkeyUserScript = {
9
9
  match: ['https://*.spankbang.com/*', 'https://*.spankbang.*/*'],
10
10
  };
11
11
 
12
- const rules = new RulesGlobal({
12
+ const rules = new Rules({
13
13
  containerSelector: '.main-container .js-media-list, .main_content_container .video-list',
14
14
  paginationStrategyOptions: {
15
15
  paginationSelector: '.paginate-bar, .pagination',
16
16
  },
17
- thumbsSelector: '.video-item:not(.clear-fix), .js-video-item',
18
- getThumbImgDataStrategy: 'auto',
19
- titleSelector: '[title]',
20
- durationSelector: '[data-testid="video-item-length"]',
17
+ thumbs: { selector: '.video-item:not(.clear-fix), .js-video-item' },
18
+ thumb: {
19
+ selectors: {
20
+ title: '[title]',
21
+ duration: '[data-testid="video-item-length"]',
22
+ }
23
+ },
24
+ thumbImg: { strategy: 'auto' },
21
25
  gropeStrategy: 'all-in-all',
22
26
  schemeOptions: ['Text Filter', 'Duration Filter', 'Badge', 'Advanced'],
23
27
  animatePreview,
@@ -3,14 +3,14 @@ import { concatMap, flatMap, map, takeWhile } from 'ix/asynciterable/operators';
3
3
  import { LSKDB } from 'lskdb';
4
4
  import type { MonkeyUserScript } from 'vite-plugin-monkey';
5
5
  import { GM_addStyle, unsafeWindow } from '$';
6
- import { getPaginationStrategy, InfiniteScroller, RulesGlobal } from '../../core';
6
+ import { getPaginationStrategy, InfiniteScroller, Rules } from '../../core';
7
7
  import {
8
8
  circularShift,
9
9
  downloader,
10
10
  fetchHtml,
11
11
  getCommonParents,
12
- objectToFormData,
13
12
  OnHover,
13
+ objectToFormData,
14
14
  parseCssUrl,
15
15
  parseHtml,
16
16
  querySelectorLastNumber,
@@ -29,7 +29,7 @@ export const meta: MonkeyUserScript = {
29
29
 
30
30
  const $ = (unsafeWindow as any).$;
31
31
 
32
- type RulesConfig = ConstructorParameters<typeof RulesGlobal>[0];
32
+ type RulesConfig = ConstructorParameters<typeof Rules>[0];
33
33
 
34
34
  const lskdb = new LSKDB();
35
35
 
@@ -56,33 +56,38 @@ function fixPlaylistThumbUrl(src: string) {
56
56
  }
57
57
 
58
58
  const defaultRulesConfig: RulesConfig = {
59
- thumbsSelector:
60
- 'div:has(> .tumbpu[title]):not(.thumbs-photo) > .tumbpu[title], .thumb-holder',
61
- getThumbImgData(thumb: HTMLElement) {
62
- const img = thumb.querySelector('img') as HTMLImageElement;
63
- const privateThumb = thumb.querySelector('.private') as HTMLElement;
59
+ thumbs: {
60
+ selector: 'div:has(> .tumbpu[title]):not(.thumbs-photo) > .tumbpu[title], .thumb-holder'
61
+ },
62
+ thumb: {
63
+ selectors: {
64
+ titleSelector: '.title',
65
+ durationSelector: '.duration',
66
+ private: { selector: '.private', type: 'boolean' },
67
+ hd: { selector: '.quality', type: 'boolean' },
68
+ views: { selector: '.view', type: 'number' },
69
+ }
70
+ },
71
+ thumbImg: {
72
+ getImgData(thumb: HTMLElement) {
73
+ const img = thumb.querySelector('img') as HTMLImageElement;
74
+ const privateThumb = thumb.querySelector('.private') as HTMLElement;
64
75
 
65
- let imgSrc = img?.getAttribute('data-original') as string;
76
+ let imgSrc = img?.getAttribute('data-original') as string;
66
77
 
67
- if (privateThumb) {
68
- imgSrc = parseCssUrl(privateThumb.style.background);
69
- privateThumb.removeAttribute('style');
70
- }
78
+ if (privateThumb) {
79
+ imgSrc = parseCssUrl(privateThumb.style.background);
80
+ privateThumb.removeAttribute('style');
81
+ }
71
82
 
72
- img.removeAttribute('data-original');
73
- img.removeAttribute('data-cnt');
74
- img.classList.remove('lazy-load');
83
+ img.removeAttribute('data-original');
84
+ img.removeAttribute('data-cnt');
85
+ img.classList.remove('lazy-load');
75
86
 
76
- return { img, imgSrc };
87
+ return { img, imgSrc };
88
+ }
77
89
  },
78
90
  containerSelectorLast: '.thumbs-items',
79
- titleSelector: '.title',
80
- durationSelector: '.duration',
81
- customThumbDataSelectors: {
82
- private: { selector: '.private', type: 'boolean' },
83
- hd: { selector: '.quality', type: 'boolean' },
84
- views: { selector: '.view', type: 'number' },
85
- },
86
91
  animatePreview,
87
92
  customDataSelectorFns: [
88
93
  'filterInclude',
@@ -131,7 +136,7 @@ const defaultRulesConfig: RulesConfig = {
131
136
  const config: RulesConfig =
132
137
  IS_MY_MEMBER_PAGE || IS_MY_WALL ? await createPrivateFeed() : defaultRulesConfig;
133
138
 
134
- const rules = new RulesGlobal(config);
139
+ const rules = new Rules(config);
135
140
 
136
141
  GM_addStyle(`
137
142
  .haveNoAccess { background: linear-gradient(to bottom, #b50000 0%, #2c2c2c 100%) red !important; }
@@ -215,7 +220,7 @@ async function friendMemberFriends(orientationFilter?: string) {
215
220
  }
216
221
  }, 60),
217
222
  )
218
- .forEach(() => {});
223
+ .forEach(() => { });
219
224
  }
220
225
 
221
226
  function initFriendship() {
@@ -498,7 +503,7 @@ async function createPrivateFeed() {
498
503
  ) => void,
499
504
  private type = 'private',
500
505
  private by: Parameters<typeof getMemberFriends>[1] = undefined,
501
- ) {}
506
+ ) { }
502
507
 
503
508
  public async *consume() {
504
509
  const membersIds = await getMemberFriends(this.id, this.by);
@@ -1,10 +1,9 @@
1
1
  import type { MonkeyUserScript } from 'vite-plugin-monkey';
2
2
  import { unsafeWindow } from '$';
3
- import { RulesGlobal } from '../../core';
3
+ import { Rules } from '../../core';
4
4
  import {
5
5
  exterminateVideo,
6
6
  fetchJson,
7
- getCommonParents,
8
7
  instantiateTemplate,
9
8
  Observer,
10
9
  OnHover,
@@ -15,10 +14,11 @@ import {
15
14
 
16
15
  export const meta: MonkeyUserScript = {
17
16
  name: 'Xhamster Improved',
18
- version: '5.0.1',
17
+ version: '5.0.2',
19
18
  description: 'Infinite scroll [optional], Filter by Title and Duration',
20
19
  match: ['https://*.xhamster.com/*', 'https://*.xhamster.*/*'],
21
20
  exclude: 'https://*.xhamster.com/embed*',
21
+ grant: ['GM_addElement', 'GM_addStyle', 'unsafeWindow']
22
22
  };
23
23
 
24
24
  const IS_VIDEO_PAGE = /^\/videos|moments\//.test(location.pathname);
@@ -45,15 +45,15 @@ function createThumb(data: Record<string, string>): string {
45
45
  return instantiateTemplate('.video-thumb', attrsToReplace, text);
46
46
  }
47
47
 
48
- const getPaginationData: RulesGlobal['getPaginationData'] = !IS_PLAYLIST
48
+ const getPaginationData: Rules['getPaginationData'] = !IS_PLAYLIST
49
49
  ? undefined
50
50
  : async (url: string): Promise<HTMLElement> => {
51
- const data = await fetchJson(url);
52
- const thumbsHtml = (data as any).list
53
- .map((e: Record<string, string>) => createThumb(e))
54
- .join('\n');
55
- return parseHtml(`<div>${thumbsHtml}</div>`);
56
- };
51
+ const data = await fetchJson(url);
52
+ const thumbsHtml = (data as any).list
53
+ .map((e: Record<string, string>) => createThumb(e))
54
+ .join('\n');
55
+ return parseHtml(`<div>${thumbsHtml}</div>`);
56
+ };
57
57
 
58
58
  function createPlaylistPaginationStrategy() {
59
59
  const collectionId = location.pathname
@@ -63,7 +63,7 @@ function createPlaylistPaginationStrategy() {
63
63
  const paginationLast = data.favoritesVideoPaging.maxPages;
64
64
  const paginationOffset = data.favoritesVideoPaging.active;
65
65
 
66
- const playlistPaginationStrategy: RulesGlobal['paginationStrategyOptions'] = {
66
+ const playlistPaginationStrategy: Rules['paginationStrategyOptions'] = {
67
67
  paginationSelector: 'nav[class *= "pagination"]',
68
68
  getPaginationLast: () => paginationLast,
69
69
  getPaginationOffset: () => paginationOffset,
@@ -75,7 +75,7 @@ function createPlaylistPaginationStrategy() {
75
75
  return playlistPaginationStrategy;
76
76
  }
77
77
 
78
- const paginationStrategyOptionsDefault: RulesGlobal['paginationStrategyOptions'] = {
78
+ const paginationStrategyOptionsDefault: Rules['paginationStrategyOptions'] = {
79
79
  paginationSelector: '.prev-next-list, .test-pager',
80
80
  };
81
81
 
@@ -83,22 +83,26 @@ const paginationStrategyOptions = IS_PLAYLIST
83
83
  ? createPlaylistPaginationStrategy()
84
84
  : paginationStrategyOptionsDefault;
85
85
 
86
- const rules = new RulesGlobal({
86
+ const rules = new Rules({
87
87
  paginationStrategyOptions,
88
88
  getPaginationData,
89
89
  containerSelectorLast: '.thumb-list',
90
- thumbsSelector: '.video-thumb',
91
- titleSelector: '.video-thumb-info__name,.video-thumb-info>a',
92
- durationSelector: '.thumb-image-container__duration',
93
- gropeStrategy: 'all-in-all',
94
- getThumbImgDataStrategy: 'auto',
95
- getThumbImgDataAttrDelete: '[loading]',
96
- customThumbDataSelectors: {
97
- watched: {
98
- type: 'boolean',
99
- selector: '[data-role="video-watched',
100
- },
90
+ thumbs: { selector: 'video-thumb' }, // [class *= video-thumb]:has(img)(and :has(~ neighbour))
91
+ thumb: {
92
+ selectors: {
93
+ title: '.video-thumb-info__name,.video-thumb-info>a',
94
+ duration: '.thumb-image-container__duration',
95
+ watched: {
96
+ type: 'boolean',
97
+ selector: '[data-role="video-watched',
98
+ },
99
+ }
100
+ },
101
+ thumbImg: {
102
+ strategy: 'auto',
103
+ shouldDelete: '[loading]',
101
104
  },
105
+ gropeStrategy: 'all-in-all',
102
106
  customDataSelectorFns: [
103
107
  'filterInclude',
104
108
  'filterExclude',
@@ -128,12 +132,14 @@ const rules = new RulesGlobal({
128
132
 
129
133
  function animatePreview() {
130
134
  function createPreviewVideoElement(src: string, mount: HTMLElement) {
131
- const video = document.createElement('video');
132
- video.playsInline = true;
133
- video.autoplay = true;
134
- video.loop = true;
135
- video.classList.add('thumb-image-container__video');
136
- video.src = src;
135
+ const video = GM_addElement('video', {
136
+ playsInline: true,
137
+ autoplay: true,
138
+ loop: true,
139
+ class: 'thumb-image-container__video',
140
+ src
141
+ });
142
+
137
143
  video.addEventListener(
138
144
  'loadeddata',
139
145
  () => {
@@ -157,7 +163,7 @@ function animatePreview() {
157
163
  }
158
164
 
159
165
  function expandMoreVideoPage() {
160
- watchElementChildrenCount(rules.container, () => setTimeout(parseThumbs, 1800));
166
+ watchElementChildrenCount(rules.container, () => setTimeout(rules.gropeInit, 1800));
161
167
  waitForElementToAppear(document.body, 'button[data-role="show-more-next"]', (el) => {
162
168
  const observer = new Observer((target) => {
163
169
  (target as HTMLButtonElement).click();
@@ -166,13 +172,6 @@ function expandMoreVideoPage() {
166
172
  });
167
173
  }
168
174
 
169
- function parseThumbs() {
170
- const containers = getCommonParents(rules.getThumbs(document.body));
171
- containers.forEach((c) => {
172
- rules.dataManager.parseData(c, c);
173
- });
174
- }
175
-
176
175
  if (IS_VIDEO_PAGE) {
177
176
  expandMoreVideoPage();
178
177
  }