pervert-monkey 1.0.11 → 1.0.13
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/README.md +22 -11
- package/dist/core/pervertmonkey.core.es.d.ts +25 -16
- package/dist/core/pervertmonkey.core.es.js +712 -61
- package/dist/core/pervertmonkey.core.es.js.map +1 -1
- package/dist/core/pervertmonkey.core.umd.js +712 -61
- package/dist/core/pervertmonkey.core.umd.js.map +1 -1
- package/dist/userscripts/3hentai.user.js +19 -26
- package/dist/userscripts/camgirlfinder.user.js +2 -2
- package/dist/userscripts/camwhores.user.js +12 -16
- package/dist/userscripts/e-hentai.user.js +3 -4
- package/dist/userscripts/ebalka.user.js +6 -11
- package/dist/userscripts/eporner.user.js +11 -16
- package/dist/userscripts/erome.user.js +2 -2
- package/dist/userscripts/eroprofile.user.js +2 -2
- package/dist/userscripts/javhdporn.user.js +2 -2
- package/dist/userscripts/missav.user.js +2 -2
- package/dist/userscripts/motherless.user.js +22 -42
- package/dist/userscripts/namethatporn.user.js +5 -4
- package/dist/userscripts/nhentai.user.js +3 -3
- package/dist/userscripts/obmenvsem.user.js +18 -4
- package/dist/userscripts/pornhub.user.js +9 -12
- package/dist/userscripts/spankbang.user.js +6 -38
- package/dist/userscripts/thisvid.user.js +20 -22
- package/dist/userscripts/xhamster.user.js +13 -16
- package/dist/userscripts/xvideos.user.js +7 -12
- package/package.json +1 -1
- package/src/core/data-handler/data-manager.ts +4 -4
- package/src/core/jabroni-config/index.ts +3 -2
- package/src/core/jabroni-config/jabroni-gui-controller.ts +61 -0
- package/src/core/parsers/thumb-data-parser.ts +10 -1
- package/src/core/rules/index.ts +12 -39
- package/src/userscripts/index.ts +1 -1
- package/src/userscripts/scripts/3hentai.ts +20 -24
- package/src/userscripts/scripts/camgirlfinder.ts +1 -1
- package/src/userscripts/scripts/camwhores.ts +10 -17
- package/src/userscripts/scripts/e-hentai.ts +1 -1
- package/src/userscripts/scripts/ebalka.ts +7 -12
- package/src/userscripts/scripts/eporner.ts +8 -14
- package/src/userscripts/scripts/erome.ts +1 -1
- package/src/userscripts/scripts/eroprofile.ts +3 -3
- package/src/userscripts/scripts/javhdporn.ts +1 -1
- package/src/userscripts/scripts/missav.ts +1 -1
- package/src/userscripts/scripts/motherless.ts +19 -48
- package/src/userscripts/scripts/namethatporn.ts +1 -1
- package/src/userscripts/scripts/nhentai.ts +1 -1
- package/src/userscripts/scripts/obmenvsem.ts +1 -1
- package/src/userscripts/scripts/pornhub.ts +1 -1
- package/src/userscripts/scripts/spankbang.ts +1 -1
- package/src/userscripts/scripts/thisvid.ts +18 -19
- package/src/userscripts/scripts/xhamster.ts +10 -11
- package/src/userscripts/scripts/xvideos.ts +6 -11
- package/src/utils/dom/index.ts +10 -0
- package/src/utils/events/on-hover.ts +17 -25
- package/src/utils/events/tick.ts +1 -3
- package/src/utils/parsers/index.ts +16 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ==UserScript==
|
|
2
2
|
// @name SpankBang.com PervertMonkey
|
|
3
3
|
// @namespace pervertmonkey
|
|
4
|
-
// @version 4.0.
|
|
4
|
+
// @version 4.0.3
|
|
5
5
|
// @author violent-orangutan
|
|
6
6
|
// @description Infinite scroll [optional]. Filter by Title and Duration
|
|
7
7
|
// @license MIT
|
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
// @supportURL https://github.com/smartacephale/sleazy-fork/issues
|
|
13
13
|
// @match https://*.spankbang.com/*
|
|
14
14
|
// @match https://*.spankbang.*/*
|
|
15
|
-
// @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.
|
|
15
|
+
// @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.13/dist/core/pervertmonkey.core.umd.js
|
|
16
16
|
// @require data:application/javascript,var core = window.pervertmonkey.core || pervertmonkey.core; var utils = core;
|
|
17
17
|
// @grant GM_addStyle
|
|
18
18
|
// @grant unsafeWindow
|
|
19
19
|
// @run-at document-idle
|
|
20
20
|
// ==/UserScript==
|
|
21
21
|
|
|
22
|
-
(function (core
|
|
22
|
+
(function (core) {
|
|
23
23
|
'use strict';
|
|
24
24
|
|
|
25
25
|
new core.Rules({
|
|
@@ -31,45 +31,13 @@
|
|
|
31
31
|
thumb: {
|
|
32
32
|
selectors: {
|
|
33
33
|
title: "[title]",
|
|
34
|
+
tags: { selector: '[data-testid="title"]', type: "string" },
|
|
34
35
|
duration: '[data-testid="video-item-length"]'
|
|
35
36
|
}
|
|
36
37
|
},
|
|
37
38
|
thumbImg: { strategy: "auto" },
|
|
38
39
|
gropeStrategy: "all-in-all",
|
|
39
|
-
schemeOptions: ["Text Filter", "Duration Filter", "Badge", "Advanced"]
|
|
40
|
-
animatePreview
|
|
40
|
+
schemeOptions: ["Text Filter", "Duration Filter", "Badge", "Advanced"]
|
|
41
41
|
});
|
|
42
|
-
function animatePreview(container) {
|
|
43
|
-
function createPreviewElement(src) {
|
|
44
|
-
return utils.parseHtml(`
|
|
45
|
-
<div class="video-js vjs-controls-disabled vjs-workinghover vjs-v7 vjs-playing vjs-has-started mp4t_video-dimensions vjs-user-inactive"
|
|
46
|
-
id="mp4t_video" tabindex="-1" lang="en" translate="no" role="region" aria-label="Video Player"
|
|
47
|
-
style="opacity: 1;">
|
|
48
|
-
<video id="mp4t_video_html5_api" class="vjs-tech" tabindex="-1" autoplay="autoplay" muted="muted" playsinline="playsinline"
|
|
49
|
-
src="${src}">
|
|
50
|
-
</video>
|
|
51
|
-
</div>`);
|
|
52
|
-
}
|
|
53
|
-
function animateThumb(e) {
|
|
54
|
-
const src = e.querySelector("[data-preview]")?.getAttribute("data-preview");
|
|
55
|
-
const vid = createPreviewElement(src);
|
|
56
|
-
e.append(vid);
|
|
57
|
-
return () => {
|
|
58
|
-
const v = vid.querySelector("video");
|
|
59
|
-
utils.exterminateVideo(v);
|
|
60
|
-
vid.remove();
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
utils.OnHover.create(
|
|
64
|
-
container,
|
|
65
|
-
(e) => e.tagName === "IMG",
|
|
66
|
-
(e) => {
|
|
67
|
-
const target = e;
|
|
68
|
-
const leaveTarget = target.closest(".thumb");
|
|
69
|
-
const onOverCallback = animateThumb(leaveTarget);
|
|
70
|
-
return { leaveTarget, onOverCallback };
|
|
71
|
-
}
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
42
|
|
|
75
|
-
})(core
|
|
43
|
+
})(core);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ==UserScript==
|
|
2
2
|
// @name ThisVid.com Improved
|
|
3
3
|
// @namespace pervertmonkey
|
|
4
|
-
// @version 8.0.
|
|
4
|
+
// @version 8.0.3
|
|
5
5
|
// @author violent-orangutan
|
|
6
6
|
// @description 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 📼
|
|
7
7
|
// @license MIT
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// @source github:smartacephale/sleazy-fork
|
|
12
12
|
// @supportURL https://github.com/smartacephale/sleazy-fork/issues
|
|
13
13
|
// @match https://*.thisvid.com/*
|
|
14
|
-
// @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.
|
|
14
|
+
// @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.13/dist/core/pervertmonkey.core.umd.js
|
|
15
15
|
// @require data:application/javascript,var core = window.pervertmonkey.core || pervertmonkey.core; var utils = core;
|
|
16
16
|
// @grant GM_addStyle
|
|
17
17
|
// @grant unsafeWindow
|
|
@@ -907,8 +907,8 @@ function takeWhile(predicate) {
|
|
|
907
907
|
},
|
|
908
908
|
thumb: {
|
|
909
909
|
selectors: {
|
|
910
|
-
|
|
911
|
-
|
|
910
|
+
title: ".title",
|
|
911
|
+
duration: ".duration",
|
|
912
912
|
private: { selector: ".private", type: "boolean" },
|
|
913
913
|
hd: { selector: ".quality", type: "boolean" },
|
|
914
914
|
views: { selector: ".view", type: "number" }
|
|
@@ -1157,24 +1157,22 @@ function takeWhile(predicate) {
|
|
|
1157
1157
|
(_2, n) => `${utils.circularShift(parseInt(n), 6)}`
|
|
1158
1158
|
);
|
|
1159
1159
|
}
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
}
|
|
1177
|
-
animate(document.querySelector(".content") || document.body);
|
|
1160
|
+
const container = document.querySelector(".content") || document.body;
|
|
1161
|
+
utils.OnHover.create(
|
|
1162
|
+
container,
|
|
1163
|
+
"div:has(> .tumbpu[title]):not(.thumbs-photo) > .tumbpu[title], .thumb-holder",
|
|
1164
|
+
(target) => {
|
|
1165
|
+
const img = target.querySelector("img");
|
|
1166
|
+
const orig = img.getAttribute("src");
|
|
1167
|
+
tick.start(
|
|
1168
|
+
() => iteratePreviewFrames(img),
|
|
1169
|
+
() => {
|
|
1170
|
+
img.src = orig;
|
|
1171
|
+
}
|
|
1172
|
+
);
|
|
1173
|
+
return () => tick.stop();
|
|
1174
|
+
}
|
|
1175
|
+
);
|
|
1178
1176
|
}
|
|
1179
1177
|
async function getMemberVideos(id, type = "private") {
|
|
1180
1178
|
const { uploadedPrivate, uploadedPublic, name } = await getMemberData(id);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ==UserScript==
|
|
2
2
|
// @name Xhamster Improved
|
|
3
3
|
// @namespace pervertmonkey
|
|
4
|
-
// @version 5.0.
|
|
4
|
+
// @version 5.0.4
|
|
5
5
|
// @author violent-orangutan
|
|
6
6
|
// @description Infinite scroll [optional], Filter by Title and Duration
|
|
7
7
|
// @license MIT
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// @match https://*.xhamster.com/*
|
|
14
14
|
// @match https://*.xhamster.*/*
|
|
15
15
|
// @exclude https://*.xhamster.com/embed*
|
|
16
|
-
// @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.
|
|
16
|
+
// @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.13/dist/core/pervertmonkey.core.umd.js
|
|
17
17
|
// @require data:application/javascript,var core = window.pervertmonkey.core || pervertmonkey.core; var utils = core;
|
|
18
18
|
// @grant GM_addElement
|
|
19
19
|
// @grant GM_addStyle
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
(function (core, utils) {
|
|
25
25
|
'use strict';
|
|
26
26
|
|
|
27
|
+
var _GM_addElement = (() => typeof GM_addElement != "undefined" ? GM_addElement : undefined)();
|
|
27
28
|
var _unsafeWindow = (() => typeof unsafeWindow != "undefined" ? unsafeWindow : undefined)();
|
|
28
29
|
|
|
29
30
|
const IS_VIDEO_PAGE = /^\/videos|moments\//.test(location.pathname);
|
|
@@ -74,8 +75,8 @@
|
|
|
74
75
|
paginationStrategyOptions,
|
|
75
76
|
getPaginationData,
|
|
76
77
|
containerSelectorLast: ".thumb-list",
|
|
77
|
-
thumbs: { selector: "video-thumb" },
|
|
78
|
-
thumb: {
|
|
78
|
+
thumbs: { selector: ".video-thumb" },
|
|
79
|
+
thumb: {
|
|
79
80
|
selectors: {
|
|
80
81
|
title: ".video-thumb-info__name,.video-thumb-info>a",
|
|
81
82
|
duration: ".thumb-image-container__duration",
|
|
@@ -87,7 +88,7 @@ thumb: {
|
|
|
87
88
|
},
|
|
88
89
|
thumbImg: {
|
|
89
90
|
strategy: "auto",
|
|
90
|
-
|
|
91
|
+
remove: "[loading]"
|
|
91
92
|
},
|
|
92
93
|
gropeStrategy: "all-in-all",
|
|
93
94
|
customDataSelectorFns: [
|
|
@@ -118,7 +119,7 @@ thumb: {
|
|
|
118
119
|
});
|
|
119
120
|
function animatePreview() {
|
|
120
121
|
function createPreviewVideoElement(src, mount) {
|
|
121
|
-
const video =
|
|
122
|
+
const video = _GM_addElement("video", {
|
|
122
123
|
playsInline: true,
|
|
123
124
|
autoplay: true,
|
|
124
125
|
loop: true,
|
|
@@ -134,16 +135,12 @@ thumb: {
|
|
|
134
135
|
);
|
|
135
136
|
return () => utils.exterminateVideo(video);
|
|
136
137
|
}
|
|
137
|
-
utils.OnHover.create(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const leaveTarget = e.parentElement?.parentElement;
|
|
144
|
-
return { leaveTarget, onOverCallback };
|
|
145
|
-
}
|
|
146
|
-
);
|
|
138
|
+
utils.OnHover.create(document.body, ".video-thumb", (e) => {
|
|
139
|
+
const container = e.querySelector(".thumb-image-container__image");
|
|
140
|
+
const videoSrc = e.querySelector("[data-previewvideo]")?.getAttribute("data-previewvideo");
|
|
141
|
+
console.log(container, videoSrc);
|
|
142
|
+
return createPreviewVideoElement(videoSrc, container);
|
|
143
|
+
});
|
|
147
144
|
}
|
|
148
145
|
function expandMoreVideoPage() {
|
|
149
146
|
utils.watchElementChildrenCount(rules.container, () => setTimeout(rules.gropeInit, 1800));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ==UserScript==
|
|
2
2
|
// @name XVideos Improved
|
|
3
3
|
// @namespace pervertmonkey
|
|
4
|
-
// @version 4.0.
|
|
4
|
+
// @version 4.0.4
|
|
5
5
|
// @author violent-orangutan
|
|
6
6
|
// @description Infinite scroll [optional], Filter by Title and Duration
|
|
7
7
|
// @license MIT
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// @source github:smartacephale/sleazy-fork
|
|
12
12
|
// @supportURL https://github.com/smartacephale/sleazy-fork/issues
|
|
13
13
|
// @match https://*.xvideos.com/*
|
|
14
|
-
// @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.
|
|
14
|
+
// @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.13/dist/core/pervertmonkey.core.umd.js
|
|
15
15
|
// @require data:application/javascript,var core = window.pervertmonkey.core || pervertmonkey.core; var utils = core;
|
|
16
16
|
// @grant GM_addStyle
|
|
17
17
|
// @grant unsafeWindow
|
|
@@ -74,16 +74,11 @@
|
|
|
74
74
|
function getVideoURL(src) {
|
|
75
75
|
return src.replace(/\w+\.\w+$/, () => "preview.mp4");
|
|
76
76
|
}
|
|
77
|
-
utils.OnHover.create(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
(
|
|
81
|
-
|
|
82
|
-
const onOverCallback = createPreviewElement(videoSrc, target);
|
|
83
|
-
const leaveTarget = target.closest(".thumb-inside");
|
|
84
|
-
return { leaveTarget, onOverCallback };
|
|
85
|
-
}
|
|
86
|
-
);
|
|
77
|
+
utils.OnHover.create(container, "div.thumb-block[id^=video_]:not(.thumb-ad)", (target) => {
|
|
78
|
+
const img = target.querySelector("img");
|
|
79
|
+
const videoSrc = getVideoURL(img.src);
|
|
80
|
+
return createPreviewElement(videoSrc, img);
|
|
81
|
+
});
|
|
87
82
|
}
|
|
88
83
|
|
|
89
84
|
})(core, utils);
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@ export class DataManager {
|
|
|
14
14
|
|
|
15
15
|
constructor(
|
|
16
16
|
private rules: Rules,
|
|
17
|
-
private
|
|
17
|
+
private parentHomogenity?: Parameters<typeof checkHomogenity>[2],
|
|
18
18
|
) {
|
|
19
19
|
this.dataFilter = new DataFilter(this.rules);
|
|
20
20
|
}
|
|
@@ -82,10 +82,10 @@ export class DataManager {
|
|
|
82
82
|
const dataOffset = this.data.size;
|
|
83
83
|
const fragment = document.createDocumentFragment();
|
|
84
84
|
const parent = container || this.rules.container;
|
|
85
|
-
const homogenity = !!this.
|
|
85
|
+
const homogenity = !!this.parentHomogenity;
|
|
86
86
|
|
|
87
87
|
for (const thumbElement of thumbs) {
|
|
88
|
-
const url = this.rules.
|
|
88
|
+
const url = this.rules.thumbDataParser.getUrl(thumbElement);
|
|
89
89
|
if (
|
|
90
90
|
!url ||
|
|
91
91
|
this.data.has(url) ||
|
|
@@ -94,7 +94,7 @@ export class DataManager {
|
|
|
94
94
|
!checkHomogenity(
|
|
95
95
|
parent,
|
|
96
96
|
thumbElement.parentElement as HTMLElement,
|
|
97
|
-
this.
|
|
97
|
+
this.parentHomogenity as object,
|
|
98
98
|
))
|
|
99
99
|
) {
|
|
100
100
|
if (removeDuplicates) thumbElement.remove();
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export * from './default-scheme'
|
|
2
|
-
export * from './default-store'
|
|
1
|
+
export * from './default-scheme';
|
|
2
|
+
export * from './default-store';
|
|
3
|
+
export * from './jabroni-gui-controller';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { JabronioStore } from 'jabroni-outfit';
|
|
2
|
+
import { map, Subject, scan, shareReplay, takeUntil } from 'rxjs';
|
|
3
|
+
import type { DataManager } from '../data-handler';
|
|
4
|
+
|
|
5
|
+
interface DirectionalEventState {
|
|
6
|
+
type?: string;
|
|
7
|
+
direction: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class JabronioGuiController {
|
|
11
|
+
constructor(
|
|
12
|
+
private store: JabronioStore,
|
|
13
|
+
private dataManager: DataManager,
|
|
14
|
+
) {
|
|
15
|
+
this.directionalEventObservable$ = this.directionalEvent();
|
|
16
|
+
this.setupStoreListeners();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private readonly destroy$ = new Subject<void>();
|
|
20
|
+
|
|
21
|
+
public dispose() {
|
|
22
|
+
this.destroy$.next();
|
|
23
|
+
this.destroy$.complete();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private directionalEventObservable$?: ReturnType<typeof this.directionalEvent>;
|
|
27
|
+
|
|
28
|
+
private directionalEvent() {
|
|
29
|
+
return this.store.eventSubject.pipe(
|
|
30
|
+
scan(
|
|
31
|
+
(acc: DirectionalEventState, value: string) => ({
|
|
32
|
+
type: value,
|
|
33
|
+
direction: acc.type === value ? !acc.direction : true,
|
|
34
|
+
}),
|
|
35
|
+
{ type: undefined, direction: true },
|
|
36
|
+
),
|
|
37
|
+
map(({ type, direction }) => ({
|
|
38
|
+
type: type as keyof typeof this.eventsMap,
|
|
39
|
+
direction,
|
|
40
|
+
})),
|
|
41
|
+
shareReplay(1),
|
|
42
|
+
takeUntil(this.destroy$),
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private readonly eventsMap = {
|
|
47
|
+
'sort by duration': (direction: boolean) =>
|
|
48
|
+
this.dataManager.sortBy('duration', direction),
|
|
49
|
+
'sort by views': (direction: boolean) => this.dataManager.sortBy('views', direction),
|
|
50
|
+
} as const;
|
|
51
|
+
|
|
52
|
+
private setupStoreListeners() {
|
|
53
|
+
this.directionalEventObservable$?.subscribe((e) => {
|
|
54
|
+
this.eventsMap[e.type](e.direction);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
this.store.stateSubject.pipe(takeUntil(this.destroy$)).subscribe((a) => {
|
|
58
|
+
this.dataManager.applyFilters(a as { [key: string]: boolean });
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
parseNumberWithLetter,
|
|
2
3
|
querySelectorLast,
|
|
3
4
|
querySelectorText,
|
|
4
5
|
sanitizeStr,
|
|
@@ -6,7 +7,7 @@ import {
|
|
|
6
7
|
} from '../../utils';
|
|
7
8
|
|
|
8
9
|
type Primitive = string | number | boolean;
|
|
9
|
-
type PrimitiveString = 'boolean' | 'string' | 'number' | 'duration';
|
|
10
|
+
type PrimitiveString = 'boolean' | 'string' | 'number' | 'float' | 'duration';
|
|
10
11
|
type ThumbData = Record<string, Primitive>;
|
|
11
12
|
type ThumbDataSelector = {
|
|
12
13
|
name: string;
|
|
@@ -25,6 +26,10 @@ export class ThumbDataParser {
|
|
|
25
26
|
return { title, duration };
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
public getUrl(thumb: HTMLElement | HTMLAnchorElement) {
|
|
30
|
+
return ((thumb.querySelector('a[href]') || thumb) as HTMLAnchorElement).href;
|
|
31
|
+
}
|
|
32
|
+
|
|
28
33
|
private preprocessCustomThumbDataSelectors() {
|
|
29
34
|
if (!this.selectors) return;
|
|
30
35
|
Object.entries(this.selectors).forEach(([key, value]) => {
|
|
@@ -66,6 +71,10 @@ export class ThumbDataParser {
|
|
|
66
71
|
if (type === 'duration') {
|
|
67
72
|
return timeToSeconds(querySelectorText(thumb, selector));
|
|
68
73
|
}
|
|
74
|
+
if (type === 'float') {
|
|
75
|
+
const value = querySelectorText(thumb, selector);
|
|
76
|
+
return parseNumberWithLetter(value);
|
|
77
|
+
}
|
|
69
78
|
return Number.parseInt(querySelectorText(thumb, selector));
|
|
70
79
|
}
|
|
71
80
|
|
package/src/core/rules/index.ts
CHANGED
|
@@ -6,20 +6,21 @@ import {
|
|
|
6
6
|
} from '../../utils';
|
|
7
7
|
import { DataManager, type DataSelectorFn } from '../data-handler';
|
|
8
8
|
import { InfiniteScroller, type OffsetGenerator } from '../infinite-scroll';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
DefaultScheme,
|
|
11
|
+
JabronioGuiController,
|
|
12
|
+
type SchemeOptions,
|
|
13
|
+
StoreStateDefault,
|
|
14
|
+
} from '../jabroni-config';
|
|
10
15
|
import {
|
|
11
16
|
getPaginationStrategy,
|
|
12
17
|
type PaginationStrategy,
|
|
13
18
|
ThumbDataParser,
|
|
14
19
|
ThumbImgParser,
|
|
15
|
-
ThumbsParser
|
|
20
|
+
ThumbsParser,
|
|
16
21
|
} from '../parsers';
|
|
17
22
|
|
|
18
23
|
export class Rules {
|
|
19
|
-
public getThumbUrl(thumb: HTMLElement | HTMLAnchorElement) {
|
|
20
|
-
return ((thumb.querySelector('a[href]') || thumb) as HTMLAnchorElement).href;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
24
|
public thumb: Parameters<typeof ThumbDataParser.create>[0] = {};
|
|
24
25
|
public thumbDataParser: ThumbDataParser;
|
|
25
26
|
|
|
@@ -73,6 +74,7 @@ export class Rules {
|
|
|
73
74
|
public schemeOptions: SchemeOptions = [];
|
|
74
75
|
public store: JabronioStore;
|
|
75
76
|
public gui: JabronioGUI;
|
|
77
|
+
public inputController: JabronioGuiController;
|
|
76
78
|
|
|
77
79
|
private createStore() {
|
|
78
80
|
const config = { ...StoreStateDefault, ...this.storeOptions };
|
|
@@ -117,35 +119,6 @@ export class Rules {
|
|
|
117
119
|
return window.self !== window.top;
|
|
118
120
|
}
|
|
119
121
|
|
|
120
|
-
private setupStoreListeners() {
|
|
121
|
-
const eventsMap = {
|
|
122
|
-
'sort by duration': {
|
|
123
|
-
action: (direction: boolean) => this.dataManager.sortBy('duration', direction),
|
|
124
|
-
},
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
let lastEvent: undefined | string;
|
|
128
|
-
let direction = true;
|
|
129
|
-
|
|
130
|
-
this.store.eventSubject.subscribe((event) => {
|
|
131
|
-
if (event === lastEvent) {
|
|
132
|
-
direction = !direction;
|
|
133
|
-
} else {
|
|
134
|
-
lastEvent = event;
|
|
135
|
-
direction = true;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (event in eventsMap) {
|
|
139
|
-
const ev = eventsMap[event as keyof typeof eventsMap];
|
|
140
|
-
ev?.action(direction);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
this.store.stateSubject.subscribe((a) => {
|
|
145
|
-
this.dataManager.applyFilters(a as { [key: string]: boolean });
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
122
|
private mutationObservers: MutationObserver[] = [];
|
|
150
123
|
|
|
151
124
|
public resetOnPaginationOrContainerDeath = true;
|
|
@@ -171,8 +144,6 @@ export class Rules {
|
|
|
171
144
|
public onResetCallback?: () => void;
|
|
172
145
|
|
|
173
146
|
private reset() {
|
|
174
|
-
// console.log('\nRESET\n');
|
|
175
|
-
|
|
176
147
|
this.mutationObservers.forEach((o) => {
|
|
177
148
|
o.disconnect();
|
|
178
149
|
});
|
|
@@ -181,7 +152,9 @@ export class Rules {
|
|
|
181
152
|
this.paginationStrategy = getPaginationStrategy(this.paginationStrategyOptions);
|
|
182
153
|
|
|
183
154
|
this.dataManager = new DataManager(this, this.dataHomogenity);
|
|
184
|
-
|
|
155
|
+
|
|
156
|
+
this.inputController.dispose();
|
|
157
|
+
this.inputController = new JabronioGuiController(this.store, this.dataManager);
|
|
185
158
|
|
|
186
159
|
this.resetInfiniteScroller();
|
|
187
160
|
|
|
@@ -209,8 +182,8 @@ export class Rules {
|
|
|
209
182
|
this.gui = this.createGui();
|
|
210
183
|
|
|
211
184
|
this.dataManager = new DataManager(this, this.dataHomogenity);
|
|
185
|
+
this.inputController = new JabronioGuiController(this.store, this.dataManager);
|
|
212
186
|
|
|
213
187
|
this.reset();
|
|
214
|
-
// console.log('data', this.dataManager.data.values().toArray());
|
|
215
188
|
}
|
|
216
189
|
}
|
package/src/userscripts/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import './scripts/
|
|
1
|
+
import './scripts/motherless'
|
|
@@ -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.4',
|
|
8
8
|
description: 'Infinite scroll [optional], Filter by Title',
|
|
9
9
|
match: 'https://*.3hentai.net/*',
|
|
10
10
|
};
|
|
@@ -12,12 +12,12 @@ export const meta: MonkeyUserScript = {
|
|
|
12
12
|
const rules = new Rules({
|
|
13
13
|
containerSelectorLast: '.listing-container',
|
|
14
14
|
thumbs: {
|
|
15
|
-
selector: '.doujin-col'
|
|
15
|
+
selector: '.doujin-col',
|
|
16
16
|
},
|
|
17
17
|
thumb: {
|
|
18
18
|
selectors: {
|
|
19
19
|
title: '.title',
|
|
20
|
-
}
|
|
20
|
+
},
|
|
21
21
|
},
|
|
22
22
|
thumbImg: {
|
|
23
23
|
strategy: 'auto',
|
|
@@ -33,27 +33,23 @@ function animatePreview() {
|
|
|
33
33
|
const end = 9999;
|
|
34
34
|
|
|
35
35
|
function rotate(src: string) {
|
|
36
|
-
return src.replace(/(\d+)(?=t\.jpg$)/,
|
|
37
|
-
(_, n) => `${circularShift(parseInt(n), end)}`);
|
|
36
|
+
return src.replace(/(\d+)(?=t\.jpg$)/, (_, n) => `${circularShift(parseInt(n), end)}`);
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
OnHover.create(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
},
|
|
57
|
-
(_) => tick.stop()
|
|
58
|
-
);
|
|
39
|
+
OnHover.create(document.body, '.doujin-col', (e) => {
|
|
40
|
+
const img = e.querySelector('img') as HTMLImageElement;
|
|
41
|
+
const origin = img.src;
|
|
42
|
+
img.src = img.src.replace(/\w+\.\w+$/, '1t.jpg');
|
|
43
|
+
img.onerror = (_) => tick.stop();
|
|
44
|
+
tick.start(
|
|
45
|
+
() => {
|
|
46
|
+
img.src = rotate(img.src);
|
|
47
|
+
},
|
|
48
|
+
() => {
|
|
49
|
+
img.src = origin;
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
return () => tick.stop();
|
|
54
|
+
});
|
|
59
55
|
}
|
|
@@ -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.2',
|
|
6
6
|
description:
|
|
7
7
|
'Adds model links for CamWhores, webcamrecordings, recu.me, camvideos, privat-zapisi',
|
|
8
8
|
match: ['https://camgirlfinder.net/*'],
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
|
|
18
18
|
export const meta: MonkeyUserScript = {
|
|
19
19
|
name: 'CamWhores PervertMonkey',
|
|
20
|
-
version: '3.0.
|
|
20
|
+
version: '3.0.6',
|
|
21
21
|
description:
|
|
22
22
|
'Infinite scroll [optional]. Filter by Title, Duration and Private/Public. Mass friend request button. Download button',
|
|
23
23
|
match: ['https://*.camwhores.tv', 'https://*.camwhores.*/*'],
|
|
@@ -48,7 +48,7 @@ const rules = new Rules({
|
|
|
48
48
|
},
|
|
49
49
|
thumbs: {
|
|
50
50
|
selector:
|
|
51
|
-
'.list-videos .item, .playlist .item, .list-playlists > div > .item, .item:has(.title)'
|
|
51
|
+
'.list-videos .item, .playlist .item, .list-playlists > div > .item, .item:has(.title)',
|
|
52
52
|
},
|
|
53
53
|
thumb: {
|
|
54
54
|
strategy: 'auto-select',
|
|
@@ -108,28 +108,21 @@ function animatePreview(container: HTMLElement) {
|
|
|
108
108
|
|
|
109
109
|
OnHover.create(
|
|
110
110
|
container,
|
|
111
|
-
(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
(_target) => {
|
|
117
|
-
const target = _target as HTMLImageElement;
|
|
118
|
-
const origin = target.src;
|
|
119
|
-
const count = parseInt(target.getAttribute('data-cnt') as string) || 5;
|
|
111
|
+
'.list-videos .item, .playlist .item, .list-playlists > div > .item, .item:has(.title)',
|
|
112
|
+
(e) => {
|
|
113
|
+
const img = e.querySelector('img') as HTMLImageElement;
|
|
114
|
+
const origin = img.src;
|
|
115
|
+
const count = parseInt(img.getAttribute('data-cnt') as string) || 5;
|
|
120
116
|
tick.start(
|
|
121
117
|
() => {
|
|
122
|
-
|
|
118
|
+
img.src = rotateImg(img.src, count);
|
|
123
119
|
},
|
|
124
120
|
() => {
|
|
125
|
-
|
|
121
|
+
img.src = origin;
|
|
126
122
|
},
|
|
127
123
|
);
|
|
128
|
-
return
|
|
129
|
-
leaveTarget: target.closest('.item') as HTMLElement,
|
|
130
|
-
};
|
|
124
|
+
return () => tick.stop();
|
|
131
125
|
},
|
|
132
|
-
() => tick.stop(),
|
|
133
126
|
);
|
|
134
127
|
}
|
|
135
128
|
|