22// @name Improve Adult Experience
33// @description Skip intros, set better default quality/duration filters, make unwanted video previews transparent, workaround load failures, make input more consistent across the websites. Supported websites: pornhub.com, xvideos.com, anysex.com, spankbang.com, porntrex.com, txxx.com, xnxx.com, xhamster.com, vxxx.com
44// @icon https://external-content.duckduckgo.com/ip3/pornhub.com.ico
5- // @version 0.41
5+ // @version 0.42
66// @downloadURL https://userscripts.codonaft.com/improve-adult-experience.user.js
77// ==/UserScript==
88
@@ -103,6 +103,16 @@ const simulateMouse = (document, node, events = ['mouseenter', 'mouseover', 'mou
103103 }
104104} ;
105105
106+ const updateUrl = ( node , href ) => {
107+ node . href = href ;
108+ node . addEventListener ( 'click' , _ => {
109+ if ( ! event . isTrusted ) return ;
110+ event . preventDefault ( ) ;
111+ event . stopImmediatePropagation ( ) ;
112+ redirect ( href , true ) ;
113+ } , true ) ;
114+ } ;
115+
106116const subscribeOnChanges = ( node , selector , f ) => {
107117 const apply = ( node , observer ) => {
108118 if ( node ?. nodeType !== 1 ) return ;
@@ -128,17 +138,23 @@ const subscribeOnChanges = (node, selector, f) => {
128138
129139 const observer = new MutationObserver ( mutations => mutations . forEach ( m => m . addedNodes . forEach ( i => apply ( i , observer ) ) ) ) ;
130140 observer . observe ( node , { childList : true , subtree : true } ) ;
131- apply ( node , observer ) ;
141+ node . querySelectorAll ( selector ) . forEach ( i => apply ( i , observer ) ) ; // TODO: apply to other scripts?
142+ } ;
143+
144+ const defaultArgs = {
145+ noKeysOverride : [ 'KeyF' , 'Space' ] ,
146+ videoSelector : 'video' ,
147+ nodeChangeSelector : 'a, div, input, li, span, video' ,
132148} ;
133149
134- const init = args => {
135- const defaultVideoSelector = 'video' ;
150+ const init = ( args = { } ) => {
136151 const {
137152 css,
138153 noKeysOverride,
139154 searchInputSelector,
140155 searchFormOrSubmitButtonSelector,
141156 onSearch,
157+ videoSelector,
142158 playSelector,
143159 pauseSelector,
144160 fullscreenSelector,
@@ -150,12 +166,9 @@ const init = args => {
150166 isUnwantedUrl,
151167 isVideoUrl,
152168 refreshOnPageChange,
169+ nodeChangeSelector,
153170 onNodeChange,
154- } = args || {
155- noKeysOverride : [ 'KeyF' , 'Space' ] ,
156- videoSelector : defaultVideoSelector ,
157- } ;
158- const videoSelector = args ?. videoSelector || defaultVideoSelector ;
171+ } = { ...defaultArgs , ...args } ;
159172
160173 try {
161174 const style = document . createElement ( 'style' ) ;
@@ -189,7 +202,7 @@ const init = args => {
189202 button = findPlayButton ( video ) ;
190203 const strategies = [
191204 _ => button ?. click ( ) ,
192- _ => simulateMouse ( document , video ) , // TODO: second attempt?
205+ _ => simulateMouse ( document , video ) ,
193206 ] ;
194207
195208 let attempt = 0 ;
@@ -271,7 +284,7 @@ const init = args => {
271284 } ;
272285
273286 let lastHref = window . location . href ;
274- subscribeOnChanges ( body , 'a, div, iframe, input, li, span, var, video' , ( node , _observer ) => {
287+ subscribeOnChanges ( body , nodeChangeSelector , ( node , _observer ) => {
275288 const newHref = window . location . href ;
276289 if ( newHref !== lastHref ) {
277290 console . log ( 'new page' , newHref ) ;
@@ -497,7 +510,7 @@ if (IGNORE_HOSTS.includes(shortDomain)) {
497510 if ( url . pathname === '/' ) return ;
498511
499512 url . searchParams . set ( 'sort' , 'top' ) ;
500- node . href = url ;
513+ updateUrl ( node , url ) ;
501514 } ,
502515 } ) ;
503516 } ,
@@ -574,7 +587,6 @@ if (IGNORE_HOSTS.includes(shortDomain)) {
574587 } ) ;
575588
576589 const fatalFallback = _ => {
577- // TODO: open the same video in the new window and close the old one?
578590 console . log ( 'fallback to embedded player' ) ;
579591 const container = body . querySelector ( 'div.playerFlvContainer' ) ;
580592 const embedUrl = `${ origin } /embed/${ params . get ( 'viewkey' ) } ` ;
@@ -606,11 +618,6 @@ if (IGNORE_HOSTS.includes(shortDomain)) {
606618 }
607619 body . classList . add ( INITIALIZED )
608620
609- if ( body . querySelector ( 'div.mgp_errorIcon' ) && body . querySelector ( 'p' ) ?. textContent . includes ( 'Please refresh the page' ) ) {
610- console . log ( 'refreshing after error' ) ;
611- refresh ( ) ;
612- }
613-
614621 const video = body . querySelector ( 'video' ) ;
615622 if ( ! video ) {
616623 console . log ( 'embedding this video is probably not allowed' ) ;
@@ -663,7 +670,7 @@ if (IGNORE_HOSTS.includes(shortDomain)) {
663670 init ( {
664671 css : `
665672 #searchSuggestions a:focus { background-color: #111111 !important }
666- div.mgp_topBar, div.mgp_thumbnailsGrid, img.mgp_pornhub, div.mgp_gridMenu { display: none !important }
673+ div.mgp_topBar, div.mgp_thumbnailsGrid, img.mgp_pornhub, div.mgp_gridMenu, ul#headerMainMenu li.photos { display: none !important }
667674 ` ,
668675 searchInputSelector : 'input#searchInput[type="text"], input[type="text"][name="search"]' ,
669676 onSearch : ( query , form ) => {
@@ -680,20 +687,16 @@ if (IGNORE_HOSTS.includes(shortDomain)) {
680687 isUnwantedDuration : text => timeToSeconds ( text ) < MIN_DURATION_MINS * 60 ,
681688 isUnwantedUrl : url => isUnwanted ( url ) ,
682689 isVideoUrl,
690+ nodeChangeSelector : `${ defaultArgs . nodeChangeSelector } , var` ,
683691 onNodeChange : node => {
684692 try {
685693 processPreview ( node ) ;
686694 } catch ( e ) {
687695 err ( e , node ) ;
688696 }
689697
690- if ( node . matches ( 'ul#headerMainMenu li.photos' ) ) {
691- node . classList . add ( HIDE ) ;
692- return ;
693- }
694-
695- if ( node . matches ( 'div.mgp_source-unavailable-screen' ) ) {
696- console . log ( 'unavailable sources' ) ;
698+ if ( node . matches ( 'div.mgp_source-unavailable-screen' ) || ( node . matches ( 'div.mgp_errorIcon' ) && body . querySelector ( 'p' ) ?. textContent ?. includes ( 'Please refresh the page' ) ) ) {
699+ console . log ( 'refreshing after error' ) ;
697700 refresh ( ) ;
698701 return ;
699702 }
@@ -726,7 +729,7 @@ if (IGNORE_HOSTS.includes(shortDomain)) {
726729 }
727730 params . set ( 'o' , p . startsWith ( '/model/' ) ? 'lg' : 'ra' ) ;
728731 }
729- setTimeout ( _ => node . href = url . toString ( ) , 500 ) ;
732+ setTimeout ( _ => updateUrl ( node , url ) , 500 ) ;
730733 } ,
731734 } ) ;
732735
@@ -739,7 +742,7 @@ if (IGNORE_HOSTS.includes(shortDomain)) {
739742 } else if ( isVideoUrl ( p ) ) {
740743 const durationFromNormalPlayer = timeToSeconds ( body . querySelector ( 'span.mgp_total' ) ?. textContent ) ;
741744 if ( durationFromNormalPlayer ) {
742- const lowQuality = ! [ ...body . querySelectorAll ( 'ul.mgp_quality li' ) ] . find ( i => i . textContent . includes ( MIN_VIDEO_HEIGHT ) ) ;
745+ const lowQuality = ! [ ...body . querySelectorAll ( 'ul.mgp_quality li' ) ] . find ( i => i . textContent ? .includes ( MIN_VIDEO_HEIGHT ) ) ;
743746 console . log ( 'low quality' , lowQuality ) ;
744747 if ( lowQuality || disliked ( body ) ) {
745748 setUnwanted ( url , Number . MAX_SAFE_INTEGER ) ;
@@ -788,13 +791,13 @@ if (IGNORE_HOSTS.includes(shortDomain)) {
788791 if ( node . closest ( 'div.sort' ) ) return ;
789792
790793 if ( href . includes ( '/search/' ) ) {
791- node . href = `${ href } /${ ending } ` ;
794+ updateUrl ( node , `${ href } /${ ending } ` ) ;
792795 } else if ( [ 'categories' , 'channels' , 'models' , 'tags' ] . find ( i => href . includes ( `/${ i } /` ) ) ) {
793- node . href = `${ href . replace ( '/hd/' , '/' ) } hd/${ ending } ` ;
796+ updateUrl ( node , `${ href . replace ( '/hd/' , '/' ) } hd/${ ending } ` ) ;
794797 } else if ( href === `${ origin } /${ topRated } /` ) {
795- node . href = `${ origin } /hd/${ ending } ` ;
798+ updateUrl ( node , `${ origin } /hd/${ ending } ` ) ;
796799 } else if ( [ 'latest-updates' , 'most-commented' , 'most-favourited' , 'most-popular' ] . find ( i => [ `${ origin } /${ i } /` , `${ origin } /hd/${ i } /` ] . includes ( href ) ) ) {
797- node . href = `${ origin . replace ( '/hd/' , '/' ) } /hd${ node . href . split ( origin ) [ 1 ] } ${ minDuration } /` ;
800+ updateUrl ( node , `${ origin . replace ( '/hd/' , '/' ) } /hd${ node . href . split ( origin ) [ 1 ] } ${ minDuration } /` ) ;
798801 }
799802 } ,
800803 } ) ;
@@ -833,7 +836,7 @@ if (IGNORE_HOSTS.includes(shortDomain)) {
833836 }
834837 params . set ( 'q' , 'fhd' ) ;
835838 params . set ( 'd' , MIN_DURATION_MINS ) ;
836- node . href = url . toString ( ) ;
839+ updateUrl ( node , url ) ;
837840 }
838841 } ,
839842 } ) ;
@@ -864,7 +867,7 @@ if (IGNORE_HOSTS.includes(shortDomain)) {
864867 url . pathname = `/search/${ page } /` ;
865868 params . set ( 'type' , 'hd' ) ;
866869 params . set ( 'duration' , '3' ) ;
867- node . href = url ;
870+ updateUrl ( node , url ) ;
868871 } else if ( [ '/categories/' , '/channel/' , '/models/' ] . find ( i => p . startsWith ( i ) ) ) {
869872 const parts = p . split ( '/' ) ;
870873 const action = parts [ 1 ] ;
@@ -877,7 +880,7 @@ if (IGNORE_HOSTS.includes(shortDomain)) {
877880 params . set ( 'date' , 'all' ) ;
878881 params . set ( 'type' , 'hd' ) ;
879882 params . set ( 'duration' , categories ? '3' : 'all' ) ;
880- node . href = url ;
883+ updateUrl ( node , url ) ;
881884 }
882885 }
883886 } ,
@@ -906,14 +909,7 @@ if (IGNORE_HOSTS.includes(shortDomain)) {
906909 url . pathname += '/1/' ;
907910 }
908911 url . searchParams . set ( 'sort' , 'top-rated' ) ;
909- node . href = url ;
910- node . addEventListener ( 'click' , _ => {
911- // TODO: use as a general solution for all updated links?
912- if ( ! event . isTrusted ) return ;
913- event . preventDefault ( ) ;
914- event . stopImmediatePropagation ( ) ;
915- redirect ( url , true ) ;
916- } , true ) ;
912+ updateUrl ( node , url ) ;
917913 }
918914 } ,
919915 } ) ;
@@ -947,10 +943,10 @@ if (IGNORE_HOSTS.includes(shortDomain)) {
947943 if ( params . get ( 'length' ) !== 'full' ) {
948944 params . set ( 'quality' , `${ MIN_VIDEO_HEIGHT } p` ) ;
949945 params . set ( 'length' , 'full' ) ;
950- node . href = url . toString ( ) ;
946+ updateUrl ( node , url ) ;
951947 }
952948 } else if ( p === '/' || [ '/categories/' , '/channels/' ] . find ( i => p . startsWith ( i ) ) ) {
953- node . href = `${ node . href . replace ( / \/ h d $ / , '/' ) } /${ best } /monthly?quality=${ MIN_VIDEO_HEIGHT } p` ;
949+ updateUrl ( node , `${ node . href . replace ( / \/ h d $ / , '/' ) } /${ best } /monthly?quality=${ MIN_VIDEO_HEIGHT } p` ) ;
954950 }
955951 } ,
956952 } ) ;
@@ -986,15 +982,15 @@ if (IGNORE_HOSTS.includes(shortDomain)) {
986982 const query = parts . slice ( hasPage ? - 2 : - 1 ) [ 0 ] || '' ;
987983 url . pathname = `${ searchPath } /${ query } /${ page } ` ;
988984 url . search = '' ;
989- node . href = url . toString ( ) ;
985+ updateUrl ( node , url ) ;
990986 }
991987 } ,
992988 } ) ;
993989 } ,
994990
995991 'xvideos.com' : _ => {
996992 init ( {
997- css : 'a.premium, div.banner-slider, div.p-red, div.quickies-lat { display: none !important }' ,
993+ css : 'a.premium, div.banner-slider, div.p-red, div.quickies-lat, div[style*="color: rgb(255, 255, 255)"][style*="text-align: center"], div.premium-results-line { display: none !important }' ,
998994 searchInputSelector : 'input.search-input[type="text"], input[type="text"][placeholder="Search X videos"]' ,
999995 onSearch : ( query , form ) => { // FIXME
1000996 const url = new URL ( form . action ) ;
@@ -1014,8 +1010,9 @@ if (IGNORE_HOSTS.includes(shortDomain)) {
10141010 isUnwantedQuality : text => ( parseFloat ( text . split ( 'p' ) [ 0 ] ) || 0 ) < MIN_VIDEO_HEIGHT ,
10151011 isUnwantedDuration : text => ! text . includes ( 'h' ) && parseFloat ( text . split ( ' min' ) [ 0 ] || 0 ) < MIN_DURATION_MINS ,
10161012 isVideoUrl : href => new URL ( href ) . pathname . startsWith ( '/video.' ) ,
1013+ nodeChangeSelector : `${ defaultArgs . nodeChangeSelector } , strong` ,
10171014 onNodeChange : node => {
1018- if ( node . matches ( 'div.error-dialog div.error-content button' ) && node . textContent . includes ( 'Retry' ) ) {
1015+ if ( node . matches ( 'div.error-dialog div.error-content button' ) && node . textContent ? .includes ( 'Retry' ) ) {
10191016 node . click ( ) ;
10201017 return ;
10211018 }
@@ -1029,17 +1026,17 @@ if (IGNORE_HOSTS.includes(shortDomain)) {
10291026 params . set ( 'sort' , 'rating' ) ;
10301027 params . set ( 'durf' , `${ MIN_DURATION_MINS } min_more` ) ;
10311028 params . set ( 'quality' , `${ MIN_VIDEO_HEIGHT } P` ) ;
1032- node . href = url . toString ( ) ;
1029+ updateUrl ( node , url ) ;
10331030 return ;
10341031 } else if ( p . startsWith ( '/c/' ) && ! p . includes ( `q:${ MIN_VIDEO_HEIGHT } P` ) ) {
10351032 const ps = p . split ( '/' ) ;
10361033 if ( ps . length >= 3 ) {
10371034 url . pathname = `${ ps [ 1 ] } /s:rating/d:${ MIN_DURATION_MINS } min_more/q:${ MIN_VIDEO_HEIGHT } P/${ ps [ 2 ] } ` ;
1038- node . href = url . toString ( ) ;
1035+ updateUrl ( node , url ) ;
10391036 }
10401037 } else if ( [ 'channels' , 'pornstars' , 'profiles' ] . find ( i => p . startsWith ( `/${ i } /` ) ) ) {
10411038 url . hash = '_tabVideos' ;
1042- node . href = url . toString ( ) ;
1039+ updateUrl ( node , url ) ;
10431040 }
10441041 } ,
10451042 } ) ;
0 commit comments