pervert-monkey 1.0.13 → 1.0.15
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 +82 -29
- package/dist/core/pervertmonkey.core.es.js +233 -115
- package/dist/core/pervertmonkey.core.es.js.map +1 -1
- package/dist/core/pervertmonkey.core.umd.js +233 -115
- package/dist/core/pervertmonkey.core.umd.js.map +1 -1
- package/dist/userscripts/3hentai.user.js +4 -11
- package/dist/userscripts/camgirlfinder.user.js +2 -2
- package/dist/userscripts/camwhores.user.js +7 -16
- package/dist/userscripts/e-hentai.user.js +3 -4
- package/dist/userscripts/ebalka.user.js +13 -5
- package/dist/userscripts/eporner.user.js +21 -41
- package/dist/userscripts/erome.user.js +9 -8
- package/dist/userscripts/eroprofile.user.js +5 -14
- package/dist/userscripts/javhdporn.user.js +6 -5
- package/dist/userscripts/missav.user.js +10 -4
- package/dist/userscripts/motherless.user.js +12 -5
- package/dist/userscripts/namethatporn.user.js +8 -14
- package/dist/userscripts/nhentai.user.js +5 -13
- package/dist/userscripts/obmenvsem.user.js +10 -4
- package/dist/userscripts/pornhub.user.js +14 -6
- package/dist/userscripts/spankbang.user.js +28 -7
- package/dist/userscripts/thisvid.user.js +12 -30
- package/dist/userscripts/xhamster.user.js +9 -14
- package/dist/userscripts/xvideos.user.js +33 -5
- package/package.json +1 -1
- package/src/core/data-handler/data-filter-fn-defaults.ts +52 -0
- package/src/core/data-handler/data-filter-fn.ts +60 -0
- package/src/core/data-handler/data-filter.ts +22 -96
- package/src/core/data-handler/data-manager.ts +75 -26
- package/src/core/jabroni-config/default-scheme.ts +54 -5
- package/src/core/jabroni-config/index.ts +1 -0
- package/src/core/jabroni-config/jabroni-gui-controller.ts +1 -1
- package/src/core/jabroni-config/scheme-selectors-mapping.ts +12 -0
- package/src/core/parsers/thumb-data-parser.ts +4 -15
- package/src/core/rules/index.ts +15 -9
- package/src/userscripts/index.ts +1 -1
- package/src/userscripts/scripts/3hentai.ts +3 -6
- package/src/userscripts/scripts/camgirlfinder.ts +1 -1
- package/src/userscripts/scripts/camwhores.ts +5 -14
- package/src/userscripts/scripts/e-hentai.ts +4 -6
- package/src/userscripts/scripts/ebalka.ts +11 -3
- package/src/userscripts/scripts/eporner.ts +20 -39
- package/src/userscripts/scripts/erome.ts +9 -7
- package/src/userscripts/scripts/eroprofile.ts +4 -12
- package/src/userscripts/scripts/javhdporn.ts +7 -8
- package/src/userscripts/scripts/missav.ts +10 -4
- package/src/userscripts/scripts/motherless.ts +11 -6
- package/src/userscripts/scripts/namethatporn.ts +8 -15
- package/src/userscripts/scripts/nhentai.ts +6 -13
- package/src/userscripts/scripts/obmenvsem.ts +9 -3
- package/src/userscripts/scripts/pornhub.ts +13 -4
- package/src/userscripts/scripts/spankbang.ts +29 -5
- package/src/userscripts/scripts/thisvid.ts +14 -29
- package/src/userscripts/scripts/xhamster.ts +9 -14
- package/src/userscripts/scripts/xvideos.ts +32 -3
- package/src/utils/parsers/index.ts +5 -2
|
@@ -3,7 +3,10 @@ import { checkHomogenity, LazyImgLoader } from '../../utils';
|
|
|
3
3
|
import type { Rules } from '../rules';
|
|
4
4
|
import { DataFilter } from './data-filter';
|
|
5
5
|
|
|
6
|
-
export type DataElement =
|
|
6
|
+
export type DataElement = {
|
|
7
|
+
element: HTMLElement;
|
|
8
|
+
[key: string]: HTMLElement | string | number | boolean;
|
|
9
|
+
};
|
|
7
10
|
|
|
8
11
|
export class DataManager {
|
|
9
12
|
public data = new Map<string, DataElement>();
|
|
@@ -14,7 +17,7 @@ export class DataManager {
|
|
|
14
17
|
|
|
15
18
|
constructor(
|
|
16
19
|
private rules: Rules,
|
|
17
|
-
private
|
|
20
|
+
private containerHomogenity?: Parameters<typeof checkHomogenity>[2],
|
|
18
21
|
) {
|
|
19
22
|
this.dataFilter = new DataFilter(this.rules);
|
|
20
23
|
}
|
|
@@ -29,10 +32,10 @@ export class DataManager {
|
|
|
29
32
|
const iterator = this.data.values().drop(offset);
|
|
30
33
|
let finished = false;
|
|
31
34
|
|
|
35
|
+
const updates: { e: HTMLElement; tag: string; condition: boolean }[] = [];
|
|
36
|
+
|
|
32
37
|
await new Promise((resolve) => {
|
|
33
38
|
function runBatch(deadline: IdleDeadline) {
|
|
34
|
-
const updates: { e: HTMLElement; tag: string; condition: boolean }[] = [];
|
|
35
|
-
|
|
36
39
|
while (deadline.timeRemaining() > 0) {
|
|
37
40
|
const { value, done } = iterator.next();
|
|
38
41
|
finished = !!done;
|
|
@@ -44,14 +47,6 @@ export class DataManager {
|
|
|
44
47
|
}
|
|
45
48
|
}
|
|
46
49
|
|
|
47
|
-
if (updates.length > 0) {
|
|
48
|
-
requestAnimationFrame(() => {
|
|
49
|
-
updates.forEach((u) => {
|
|
50
|
-
u.e.classList.toggle(u.tag, u.condition);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
50
|
if (!finished) {
|
|
56
51
|
requestIdleCallback(runBatch);
|
|
57
52
|
} else {
|
|
@@ -61,6 +56,32 @@ export class DataManager {
|
|
|
61
56
|
|
|
62
57
|
requestIdleCallback(runBatch);
|
|
63
58
|
});
|
|
59
|
+
|
|
60
|
+
const parents = new Set(updates.map((u) => u.e.parentElement));
|
|
61
|
+
|
|
62
|
+
requestAnimationFrame(() => {
|
|
63
|
+
const revertDisplayStyle = [...parents].map((p) => {
|
|
64
|
+
const display = p?.style.display;
|
|
65
|
+
if (!display) return undefined;
|
|
66
|
+
p.style.display = 'none';
|
|
67
|
+
p.style.contain = 'layout style paint';
|
|
68
|
+
p.style.willChange = 'contents';
|
|
69
|
+
return () => {
|
|
70
|
+
p.style.display = display;
|
|
71
|
+
requestAnimationFrame(() => {
|
|
72
|
+
p.style.willChange = 'auto';
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
updates.forEach((u) => {
|
|
78
|
+
u.e.classList.toggle(u.tag, u.condition);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
revertDisplayStyle.forEach((f) => {
|
|
82
|
+
f?.();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
64
85
|
};
|
|
65
86
|
|
|
66
87
|
public filterAll = async (offset?: number): Promise<void> => {
|
|
@@ -82,7 +103,12 @@ export class DataManager {
|
|
|
82
103
|
const dataOffset = this.data.size;
|
|
83
104
|
const fragment = document.createDocumentFragment();
|
|
84
105
|
const parent = container || this.rules.container;
|
|
85
|
-
const homogenity = !!this.
|
|
106
|
+
const homogenity = !!this.containerHomogenity;
|
|
107
|
+
|
|
108
|
+
if (parent) {
|
|
109
|
+
parent.style.contain = 'layout style paint';
|
|
110
|
+
parent.style.willChange = 'contents';
|
|
111
|
+
}
|
|
86
112
|
|
|
87
113
|
for (const thumbElement of thumbs) {
|
|
88
114
|
const url = this.rules.thumbDataParser.getUrl(thumbElement);
|
|
@@ -94,7 +120,7 @@ export class DataManager {
|
|
|
94
120
|
!checkHomogenity(
|
|
95
121
|
parent,
|
|
96
122
|
thumbElement.parentElement as HTMLElement,
|
|
97
|
-
this.
|
|
123
|
+
this.containerHomogenity as object,
|
|
98
124
|
))
|
|
99
125
|
) {
|
|
100
126
|
if (removeDuplicates) thumbElement.remove();
|
|
@@ -114,7 +140,7 @@ export class DataManager {
|
|
|
114
140
|
|
|
115
141
|
this.filterAll(dataOffset).then(() => {
|
|
116
142
|
requestAnimationFrame(() => {
|
|
117
|
-
parent
|
|
143
|
+
parent?.appendChild(fragment);
|
|
118
144
|
});
|
|
119
145
|
});
|
|
120
146
|
};
|
|
@@ -122,23 +148,46 @@ export class DataManager {
|
|
|
122
148
|
public sortBy<K extends keyof DataElement>(key: K, direction = true): void {
|
|
123
149
|
if (this.data.size < 2) return;
|
|
124
150
|
|
|
125
|
-
|
|
151
|
+
const elements = this.data
|
|
126
152
|
.values()
|
|
127
153
|
.toArray()
|
|
128
|
-
.
|
|
129
|
-
|
|
130
|
-
});
|
|
154
|
+
.filter((e) => e.element.parentElement !== null)
|
|
155
|
+
.map((e) => e);
|
|
131
156
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
157
|
+
const containers = new Set(elements.map((e) => e.element.parentElement as HTMLElement));
|
|
158
|
+
containers.forEach((c) => {
|
|
159
|
+
c.style.contain = 'layout style paint';
|
|
160
|
+
c.style.willChange = 'contents';
|
|
161
|
+
});
|
|
135
162
|
|
|
136
|
-
|
|
163
|
+
const elementsByContainers = new Map<HTMLElement, DataElement[]>();
|
|
164
|
+
containers.forEach((c) => {
|
|
165
|
+
elementsByContainers.set(c, []);
|
|
166
|
+
});
|
|
137
167
|
|
|
138
|
-
|
|
139
|
-
|
|
168
|
+
elements.forEach((e) => {
|
|
169
|
+
const parent = e.element.parentElement as HTMLElement;
|
|
170
|
+
const container = elementsByContainers.get(parent);
|
|
171
|
+
container?.push(e);
|
|
140
172
|
});
|
|
141
173
|
|
|
142
|
-
|
|
174
|
+
const dir = direction ? -1 : 1;
|
|
175
|
+
|
|
176
|
+
for (const [container, items] of elementsByContainers) {
|
|
177
|
+
items.sort((a, b) => ((a[key] as number) - (b[key] as number)) * dir);
|
|
178
|
+
const domNodes = items.map((e) => e.element);
|
|
179
|
+
|
|
180
|
+
const display = container.style.display;
|
|
181
|
+
container.style.display = 'none';
|
|
182
|
+
|
|
183
|
+
container.replaceChildren(...domNodes);
|
|
184
|
+
|
|
185
|
+
requestAnimationFrame(() => {
|
|
186
|
+
container.style.display = display;
|
|
187
|
+
requestAnimationFrame(() => {
|
|
188
|
+
container.style.willChange = 'auto';
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
143
192
|
}
|
|
144
193
|
}
|
|
@@ -2,7 +2,7 @@ import type { JabroniTypes, SchemeInput, setupScheme } from 'jabroni-outfit';
|
|
|
2
2
|
|
|
3
3
|
export const DefaultScheme = [
|
|
4
4
|
{
|
|
5
|
-
title: '
|
|
5
|
+
title: 'Title Filter',
|
|
6
6
|
collapsed: true,
|
|
7
7
|
content: [
|
|
8
8
|
{ filterExclude: false, label: 'exclude' },
|
|
@@ -21,6 +21,26 @@ export const DefaultScheme = [
|
|
|
21
21
|
},
|
|
22
22
|
],
|
|
23
23
|
},
|
|
24
|
+
{
|
|
25
|
+
title: 'Uploader Filter',
|
|
26
|
+
collapsed: true,
|
|
27
|
+
content: [
|
|
28
|
+
{ filterUploaderExclude: false, label: 'exclude' },
|
|
29
|
+
{
|
|
30
|
+
filterUploaderExcludeWords: '',
|
|
31
|
+
label: 'keywords',
|
|
32
|
+
watch: 'filterUploaderExclude',
|
|
33
|
+
placeholder: 'word, f:full_word, r:RegEx...',
|
|
34
|
+
},
|
|
35
|
+
{ filterUploaderInclude: false, label: 'include' },
|
|
36
|
+
{
|
|
37
|
+
filterUploaderIncludeWords: '',
|
|
38
|
+
label: 'keywords',
|
|
39
|
+
watch: 'filterUploaderInclude',
|
|
40
|
+
placeholder: 'word, f:full_word, r:RegEx...',
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
},
|
|
24
44
|
{
|
|
25
45
|
title: 'Duration Filter',
|
|
26
46
|
collapsed: true,
|
|
@@ -42,6 +62,7 @@ export const DefaultScheme = [
|
|
|
42
62
|
},
|
|
43
63
|
{
|
|
44
64
|
title: 'Sort By',
|
|
65
|
+
collapsed: true,
|
|
45
66
|
content: [
|
|
46
67
|
{
|
|
47
68
|
'sort by views': () => {},
|
|
@@ -51,14 +72,40 @@ export const DefaultScheme = [
|
|
|
51
72
|
},
|
|
52
73
|
],
|
|
53
74
|
},
|
|
75
|
+
{
|
|
76
|
+
title: 'Sort By Duration',
|
|
77
|
+
collapsed: true,
|
|
78
|
+
content: [
|
|
79
|
+
{
|
|
80
|
+
'sort by duration': () => {},
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
title: 'Sort By Views',
|
|
86
|
+
collapsed: true,
|
|
87
|
+
content: [
|
|
88
|
+
{
|
|
89
|
+
'sort by views': () => {},
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
},
|
|
54
93
|
{
|
|
55
94
|
title: 'Privacy Filter',
|
|
95
|
+
collapsed: true,
|
|
56
96
|
content: [
|
|
57
97
|
{ filterPrivate: false, label: 'private' },
|
|
58
98
|
{ filterPublic: false, label: 'public' },
|
|
59
99
|
{ 'check access 🔓': () => {} },
|
|
60
100
|
],
|
|
61
101
|
},
|
|
102
|
+
{
|
|
103
|
+
title: 'HD Filter',
|
|
104
|
+
content: [
|
|
105
|
+
{ filterHD: false, label: 'hd' },
|
|
106
|
+
{ filterNonHD: false, label: 'non-hd' },
|
|
107
|
+
],
|
|
108
|
+
},
|
|
62
109
|
{
|
|
63
110
|
title: 'Advanced',
|
|
64
111
|
collapsed: true,
|
|
@@ -79,6 +126,9 @@ export const DefaultScheme = [
|
|
|
79
126
|
writeHistory: false,
|
|
80
127
|
label: 'write history',
|
|
81
128
|
},
|
|
129
|
+
{
|
|
130
|
+
reset: () => { localStorage.removeItem('state_acephale'); }
|
|
131
|
+
}
|
|
82
132
|
],
|
|
83
133
|
},
|
|
84
134
|
{
|
|
@@ -92,7 +142,6 @@ export const DefaultScheme = [
|
|
|
92
142
|
},
|
|
93
143
|
] as const satisfies SchemeInput;
|
|
94
144
|
|
|
95
|
-
export type
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
)[];
|
|
145
|
+
export type SchemeKeys = JabroniTypes.ExtractValuesByKey<typeof DefaultScheme, 'title'>;
|
|
146
|
+
|
|
147
|
+
export type SchemeOptions = (Parameters<typeof setupScheme>[0][0] | SchemeKeys)[];
|
|
@@ -51,7 +51,7 @@ export class JabronioGuiController {
|
|
|
51
51
|
|
|
52
52
|
private setupStoreListeners() {
|
|
53
53
|
this.directionalEventObservable$?.subscribe((e) => {
|
|
54
|
-
this.eventsMap[e.type](e.direction);
|
|
54
|
+
this.eventsMap[e.type]?.(e.direction);
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
this.store.stateSubject.pipe(takeUntil(this.destroy$)).subscribe((a) => {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defaultDataFilterFns } from '../data-handler/data-filter-fn-defaults';
|
|
2
|
+
import { DefaultScheme, type SchemeKeys } from './default-scheme';
|
|
3
|
+
|
|
4
|
+
export function getSelectorFnsFromScheme(xs: SchemeKeys[]) {
|
|
5
|
+
const keys = xs.flatMap((s) => {
|
|
6
|
+
const schemeBlock = DefaultScheme.find((e) => e.title === s);
|
|
7
|
+
if (!schemeBlock) return [];
|
|
8
|
+
return schemeBlock.content.flatMap((c) => Object.keys(c));
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
return keys.filter((k) => k in defaultDataFilterFns);
|
|
12
|
+
}
|
|
@@ -56,6 +56,7 @@ export class ThumbDataParser {
|
|
|
56
56
|
selector: '[class *= uploader], [class *= user], [class *= name]',
|
|
57
57
|
},
|
|
58
58
|
{ name: 'duration', type: 'duration', selector: '[class *= duration]' },
|
|
59
|
+
// { name: 'views', type: 'float', selector: '[class *= view]' },
|
|
59
60
|
];
|
|
60
61
|
|
|
61
62
|
private getThumbDataWith(
|
|
@@ -82,17 +83,14 @@ export class ThumbDataParser {
|
|
|
82
83
|
public strategy: 'manual' | 'auto-select' | 'auto-text' = 'manual',
|
|
83
84
|
public selectors: ThumbDataSelectorsRaw = {},
|
|
84
85
|
public callback?: (thumb: HTMLElement, thumbData: ThumbData) => void,
|
|
85
|
-
public stringsMeltInTitle = true,
|
|
86
86
|
) {
|
|
87
87
|
this.preprocessCustomThumbDataSelectors();
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
public static create(
|
|
91
|
-
o: Partial<
|
|
92
|
-
Pick<ThumbDataParser, 'strategy' | 'selectors' | 'callback' | 'stringsMeltInTitle'>
|
|
93
|
-
> = {},
|
|
91
|
+
o: Partial<Pick<ThumbDataParser, 'strategy' | 'selectors' | 'callback'>> = {},
|
|
94
92
|
) {
|
|
95
|
-
return new ThumbDataParser(o.strategy, o.selectors, o.callback
|
|
93
|
+
return new ThumbDataParser(o.strategy, o.selectors, o.callback);
|
|
96
94
|
}
|
|
97
95
|
|
|
98
96
|
public getThumbData(thumb: HTMLElement): ThumbData {
|
|
@@ -101,22 +99,13 @@ export class ThumbDataParser {
|
|
|
101
99
|
}
|
|
102
100
|
|
|
103
101
|
if (this.strategy === 'auto-select') {
|
|
104
|
-
this.thumbDataSelectors
|
|
102
|
+
this.thumbDataSelectors.push(...this.defaultThumbDataSelectors);
|
|
105
103
|
}
|
|
106
104
|
|
|
107
105
|
const thumbData = Object.fromEntries(
|
|
108
106
|
this.thumbDataSelectors.map((s) => [s.name, this.getThumbDataWith(thumb, s)]),
|
|
109
107
|
);
|
|
110
108
|
|
|
111
|
-
if (this.stringsMeltInTitle) {
|
|
112
|
-
Object.entries(thumbData).forEach(([k, v]) => {
|
|
113
|
-
if (typeof v === 'string' && k !== 'title') {
|
|
114
|
-
thumbData.title = `${thumbData.title} ${k}:${v}`;
|
|
115
|
-
delete thumbData[k];
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
109
|
this.callback?.(thumb, thumbData);
|
|
121
110
|
|
|
122
111
|
return thumbData;
|
package/src/core/rules/index.ts
CHANGED
|
@@ -4,10 +4,12 @@ import {
|
|
|
4
4
|
querySelectorLast,
|
|
5
5
|
waitForElementToDisappear,
|
|
6
6
|
} from '../../utils';
|
|
7
|
-
import { DataManager
|
|
7
|
+
import { DataManager } from '../data-handler';
|
|
8
|
+
import type { DataFilterFnFrom } from '../data-handler/data-filter-fn';
|
|
8
9
|
import { InfiniteScroller, type OffsetGenerator } from '../infinite-scroll';
|
|
9
10
|
import {
|
|
10
11
|
DefaultScheme,
|
|
12
|
+
getSelectorFnsFromScheme,
|
|
11
13
|
JabronioGuiController,
|
|
12
14
|
type SchemeOptions,
|
|
13
15
|
StoreStateDefault,
|
|
@@ -61,12 +63,15 @@ export class Rules {
|
|
|
61
63
|
public paginationStrategy: PaginationStrategy;
|
|
62
64
|
|
|
63
65
|
public dataManager: DataManager;
|
|
64
|
-
public
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
public containerHomogenity: ConstructorParameters<typeof DataManager>[1];
|
|
67
|
+
|
|
68
|
+
public customDataFilterFns: (Record<string, DataFilterFnFrom<any>> | string)[] = [];
|
|
69
|
+
private hookDataFilterFns() {
|
|
70
|
+
const defaultFilterFns = getSelectorFnsFromScheme(
|
|
71
|
+
this.schemeOptions.filter((s) => typeof s === 'string'),
|
|
72
|
+
);
|
|
73
|
+
this.customDataFilterFns.push(...defaultFilterFns);
|
|
74
|
+
}
|
|
70
75
|
|
|
71
76
|
public animatePreview?: (doc: HTMLElement) => void;
|
|
72
77
|
|
|
@@ -151,7 +156,7 @@ export class Rules {
|
|
|
151
156
|
|
|
152
157
|
this.paginationStrategy = getPaginationStrategy(this.paginationStrategyOptions);
|
|
153
158
|
|
|
154
|
-
this.dataManager = new DataManager(this, this.
|
|
159
|
+
this.dataManager = new DataManager(this, this.containerHomogenity);
|
|
155
160
|
|
|
156
161
|
this.inputController.dispose();
|
|
157
162
|
this.inputController = new JabronioGuiController(this.store, this.dataManager);
|
|
@@ -181,7 +186,8 @@ export class Rules {
|
|
|
181
186
|
this.store = this.createStore();
|
|
182
187
|
this.gui = this.createGui();
|
|
183
188
|
|
|
184
|
-
this.
|
|
189
|
+
this.hookDataFilterFns();
|
|
190
|
+
this.dataManager = new DataManager(this, this.containerHomogenity);
|
|
185
191
|
this.inputController = new JabronioGuiController(this.store, this.dataManager);
|
|
186
192
|
|
|
187
193
|
this.reset();
|
package/src/userscripts/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import './scripts/
|
|
1
|
+
import './scripts/ebalka';
|
|
@@ -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.7',
|
|
8
8
|
description: 'Infinite scroll [optional], Filter by Title',
|
|
9
9
|
match: 'https://*.3hentai.net/*',
|
|
10
10
|
};
|
|
@@ -23,8 +23,7 @@ const rules = new Rules({
|
|
|
23
23
|
strategy: 'auto',
|
|
24
24
|
},
|
|
25
25
|
gropeStrategy: 'all-in-all',
|
|
26
|
-
|
|
27
|
-
schemeOptions: ['Text Filter', 'Badge', 'Advanced'],
|
|
26
|
+
schemeOptions: ['Title Filter', 'Badge', 'Advanced'],
|
|
28
27
|
animatePreview,
|
|
29
28
|
});
|
|
30
29
|
|
|
@@ -42,9 +41,7 @@ function animatePreview() {
|
|
|
42
41
|
img.src = img.src.replace(/\w+\.\w+$/, '1t.jpg');
|
|
43
42
|
img.onerror = (_) => tick.stop();
|
|
44
43
|
tick.start(
|
|
45
|
-
() =>
|
|
46
|
-
img.src = rotate(img.src);
|
|
47
|
-
},
|
|
44
|
+
() => img.setAttribute('src', img.src),
|
|
48
45
|
() => {
|
|
49
46
|
img.src = origin;
|
|
50
47
|
},
|
|
@@ -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.5',
|
|
6
6
|
description:
|
|
7
7
|
'Adds model links for CamWhores, webcamrecordings, recu.me, camvideos, privat-zapisi',
|
|
8
8
|
match: ['https://camgirlfinder.net/*'],
|
|
@@ -17,9 +17,9 @@ import {
|
|
|
17
17
|
|
|
18
18
|
export const meta: MonkeyUserScript = {
|
|
19
19
|
name: 'CamWhores PervertMonkey',
|
|
20
|
-
version: '3.0.
|
|
20
|
+
version: '3.0.9',
|
|
21
21
|
description:
|
|
22
|
-
'Infinite scroll [optional]. Filter by Title, Duration and Private/Public. Mass friend request button. Download button',
|
|
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.*/*'],
|
|
24
24
|
exclude: 'https://*.camwhores.tv/*mode=async*',
|
|
25
25
|
};
|
|
@@ -54,27 +54,18 @@ const rules = new Rules({
|
|
|
54
54
|
strategy: 'auto-select',
|
|
55
55
|
selectors: {
|
|
56
56
|
private: { type: 'boolean', selector: '[class*=private]' },
|
|
57
|
+
views: { selector: '.views', type: 'float' },
|
|
57
58
|
},
|
|
58
59
|
},
|
|
59
60
|
thumbImg: {
|
|
60
61
|
selector: 'data-original',
|
|
61
62
|
},
|
|
62
63
|
gropeStrategy: 'all-in-all',
|
|
63
|
-
customDataSelectorFns: [
|
|
64
|
-
'filterInclude',
|
|
65
|
-
'filterExclude',
|
|
66
|
-
'filterDuration',
|
|
67
|
-
{
|
|
68
|
-
filterPrivate: (e, state) => (state.filterPrivate && e.private) as boolean,
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
filterPublic: (e, state) => (state.filterPublic && !e.private) as boolean,
|
|
72
|
-
},
|
|
73
|
-
],
|
|
74
64
|
schemeOptions: [
|
|
75
|
-
'
|
|
65
|
+
'Title Filter',
|
|
76
66
|
'Duration Filter',
|
|
77
67
|
'Privacy Filter',
|
|
68
|
+
'Sort By',
|
|
78
69
|
'Badge',
|
|
79
70
|
{
|
|
80
71
|
title: 'Advanced',
|
|
@@ -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.
|
|
7
|
+
version: '1.0.5',
|
|
8
8
|
description: 'Infinite scroll [optional], Filter by Title',
|
|
9
9
|
match: ['https://*.e-hentai.org/*'],
|
|
10
10
|
};
|
|
@@ -13,16 +13,15 @@ const rules = new Rules({
|
|
|
13
13
|
thumbs: { selector: '.gl1t' },
|
|
14
14
|
thumb: {
|
|
15
15
|
selectors: {
|
|
16
|
-
title: '.glname'
|
|
17
|
-
}
|
|
16
|
+
title: '.glname',
|
|
17
|
+
},
|
|
18
18
|
},
|
|
19
19
|
thumbImg: {
|
|
20
20
|
selector: 'data-lazy-load',
|
|
21
21
|
},
|
|
22
22
|
containerSelectorLast: '.itg.gld',
|
|
23
23
|
paginationStrategyOptions: createPaginationStrategyOptions(),
|
|
24
|
-
|
|
25
|
-
schemeOptions: ['Text Filter', 'Badge', 'Advanced'],
|
|
24
|
+
schemeOptions: ['Title Filter', 'Badge', 'Advanced'],
|
|
26
25
|
});
|
|
27
26
|
|
|
28
27
|
function createPaginationStrategyOptions(): Rules['paginationStrategyOptions'] {
|
|
@@ -39,7 +38,6 @@ function createPaginationStrategyOptions(): Rules['paginationStrategyOptions'] {
|
|
|
39
38
|
nextLink = getNextLink();
|
|
40
39
|
return nextLink;
|
|
41
40
|
}
|
|
42
|
-
// need cache or reuse infinite scroller request
|
|
43
41
|
const doc = await fetchHtml(nextLink);
|
|
44
42
|
nextLink = getNextLink(doc);
|
|
45
43
|
return nextLink;
|
|
@@ -4,8 +4,8 @@ import { exterminateVideo, OnHover, parseHtml } from '../../utils';
|
|
|
4
4
|
|
|
5
5
|
export const meta: MonkeyUserScript = {
|
|
6
6
|
name: 'Ebalka PervertMonkey',
|
|
7
|
-
version: '3.0.
|
|
8
|
-
description: 'Infinite scroll [optional], Filter by Title and Duration',
|
|
7
|
+
version: '3.0.6',
|
|
8
|
+
description: 'Infinite scroll [optional], Filter by Title and Duration, Sort by Duration',
|
|
9
9
|
match: [
|
|
10
10
|
'https://b.ebalka.zip/*',
|
|
11
11
|
'https://a.ebalka.love/*',
|
|
@@ -27,10 +27,18 @@ const rules = new Rules({
|
|
|
27
27
|
selectors: {
|
|
28
28
|
title: '.card__title',
|
|
29
29
|
duration: '.card__spot > span:last-child',
|
|
30
|
+
hd: { selector: '.card__icons > .card__icon', type: 'boolean' },
|
|
30
31
|
},
|
|
31
32
|
},
|
|
32
33
|
animatePreview,
|
|
33
|
-
schemeOptions: [
|
|
34
|
+
schemeOptions: [
|
|
35
|
+
'Title Filter',
|
|
36
|
+
'Duration Filter',
|
|
37
|
+
'HD Filter',
|
|
38
|
+
'Sort By Duration',
|
|
39
|
+
'Badge',
|
|
40
|
+
'Advanced',
|
|
41
|
+
],
|
|
34
42
|
});
|
|
35
43
|
|
|
36
44
|
function animatePreview(container: HTMLElement) {
|
|
@@ -5,8 +5,9 @@ import { OnHover } from '../../utils';
|
|
|
5
5
|
|
|
6
6
|
export const meta: MonkeyUserScript = {
|
|
7
7
|
name: 'Eporner PervertMonkey',
|
|
8
|
-
version: '2.0.
|
|
9
|
-
description:
|
|
8
|
+
version: '2.0.7',
|
|
9
|
+
description:
|
|
10
|
+
'Infinite scroll [optional], Filter by Title, Uploader, Duration and HD, Sort by Views and Duration',
|
|
10
11
|
match: ['https://*.eporner.com/*', 'https://*.eporner.*/*'],
|
|
11
12
|
};
|
|
12
13
|
|
|
@@ -19,10 +20,11 @@ const rules = new Rules({
|
|
|
19
20
|
thumbs: { selector: 'div[id^=vf][data-id]' },
|
|
20
21
|
thumb: {
|
|
21
22
|
selectors: {
|
|
22
|
-
quality: { type: 'number', selector: '[title="Quality"]' },
|
|
23
23
|
title: 'a',
|
|
24
24
|
uploader: '[title="Uploader"]',
|
|
25
25
|
duration: '[title="Duration"]',
|
|
26
|
+
views: { selector: '[title="Views"]', type: 'float' },
|
|
27
|
+
quality: { selector: '[title="Quality"]', type: 'number' },
|
|
26
28
|
},
|
|
27
29
|
},
|
|
28
30
|
thumbImg: {
|
|
@@ -30,50 +32,29 @@ const rules = new Rules({
|
|
|
30
32
|
remove: 'auto',
|
|
31
33
|
},
|
|
32
34
|
containerSelectorLast: '#vidresults',
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
{
|
|
38
|
-
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
quality480: (el, state) => !!state.quality480 && el.quality !== 480,
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
quality720: (el, state) => !!state.quality720 && el.quality !== 720,
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
quality1080: (el, state) => !!state.quality1080 && el.quality !== 1080,
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
quality4k: (el, state) => !!state.quality4k && el.quality !== 4,
|
|
51
|
-
},
|
|
35
|
+
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
|
+
{ quality4k: (el, state) => !!state.quality4k && el.quality !== 4 },
|
|
52
41
|
],
|
|
53
42
|
schemeOptions: [
|
|
54
|
-
'
|
|
55
|
-
'
|
|
43
|
+
'Title Filter',
|
|
44
|
+
'Uploader Filter',
|
|
56
45
|
'Duration Filter',
|
|
57
46
|
{
|
|
58
47
|
title: 'Quality Filter ',
|
|
59
48
|
content: [
|
|
60
|
-
{
|
|
61
|
-
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
quality720: false,
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
quality1080: false,
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
quality4k: false,
|
|
74
|
-
},
|
|
49
|
+
{ quality360: false },
|
|
50
|
+
{ quality480: false },
|
|
51
|
+
{ quality720: false },
|
|
52
|
+
{ quality1080: false },
|
|
53
|
+
{ quality4k: false },
|
|
75
54
|
],
|
|
76
55
|
},
|
|
56
|
+
'Sort By',
|
|
57
|
+
'Badge',
|
|
77
58
|
'Advanced',
|
|
78
59
|
],
|
|
79
60
|
animatePreview,
|