diff --git a/DIU.meta.js b/DIU.meta.js index 141613a..36b1dd7 100644 --- a/DIU.meta.js +++ b/DIU.meta.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Digitally Imported Userscript // @namespace LTKDIFMU -// @version 2017.10.0 +// @version 2018.1.0 // @author LethaK Maas // @description Removes afk popup and minimize ads if possible // @include https://*.di.fm* diff --git a/DIU.user.js b/DIU.user.js index fd359a0..cb1635c 100644 --- a/DIU.user.js +++ b/DIU.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Digitally Imported Userscript // @namespace LTKDIFMU -// @version 2017.10.0 +// @version 2018.1.0 // @author LethaK Maas // @description Removes afk popup and minimize ads if possible // @include https://*.di.fm* @@ -16,8 +16,34 @@ // @run-at document-start // ==/UserScript== (function(){ - console.warn('[DIUserscript] Initializing'); + console.log('[DIUserscript] Initializing...'); + document.addEventListener("DOMContentLoaded", function(event) { + console.log('[DIUserscript] window.document ready'); + + redefineCommands(); + redefineReqres(); + redefineBlockAdBlock(); + redefineOptions(); + adSilencer(); + initTimeout(); + initInterval(); + + addVolumeNumericInput(); // comment this line if you don't want this feature + + + + console.log('[DIUserscript] Initialized'); + }); + + /** + * Utility function + * + * @param obj + * @param propName + * @param propValue + * @returns {*} + */ var defineProp = function(obj, propName, propValue){ delete obj[propName]; Object.defineProperty(obj, propName, { @@ -29,8 +55,83 @@ return obj; }; + /** + * Seems useful to identify if we are on DI or the other sites. + * Not required at the moment + * + * @returns string di|jazzradio|radiotunes|classicalradio|rockradio + */ + var getCurrentSiteKey = function() { + return di.app.options.network_key; + }; + + /** + * WebPlayer Utilities + * + * @type {{getCurentVolume: getCurentVolume, setVolume: setVolume}} + */ + var WebPlayer = { + event: { + onVolumeChange: function(func) { + console.log('[DIUserscript] WebPlayer.event.onVolumeChange (new handler)'); + if (typeof func !== 'function') { + var func = function(e,value){}; + } + di.app.WebplayerApp.model.on('change:volume', func); + } + }, + getVolume: function() { + return di.app.reqres.request('webplayer:volume'); + }, + setVolume: function(volume) { + console.log('[DIUserscript] WebPlayer.setVolume ', volume); + if (typeof volume === 'undefined') { + var volume = 10; + } + if (volume < 0) { + volume = 0; + } + if (volume > 100) { + volume = 100; + } + + di.app.commands.execute('webplayer:volume', volume); + // di.app.WebplayerApp.model.setVolume(value); + } + }; + + var removeHomeHeroPremiumVisualAds = function() { + var $bannersAnchors = jQuery('.banner a[href^="/premium?"]', '#hero.home'); + var iToRemove = 0 + $bannersAnchors.length; + var iRemoved = 0; + $bannersAnchors.each(function(i,v) { + jQuery(v).parents('.banner').remove(); + iRemoved = iRemoved + 1; + }); + + if (iRemoved > 0) { + console.warn('[DIUserscript] removeHomeHeroPremiumVisualAds (remove count): ', iRemoved); + } + }; + + /** + * This seems like a good idea to help keeping ads away + */ + var redefineOptions = function() { + console.log('[DIUserscript] redefineOptions'); + + defineProp(di.app.options.advertising, 'ad_repeat_cap', 0); + defineProp(di.app.options.advertising, 'interruptible_track_length', 13371337); + defineProp(di.app.options.advertising, 'interruptible_track_grace_period', 13371337); + defineProp(di.app.options.advertising, 'midroll_banner_continue_delay', 0); + defineProp(di.app.options, 'registration_wall', null); + }; + + /** + * Silencing audio ads whenever they get played. + */ var adSilencer = function(){ - console.warn('[DIUserscript] adSilencer'); + console.log('[DIUserscript] adSilencer'); // Silence ads whenever they get played ... (should not be happening, but we never know, the rascals ;) di.app.vent.on('webplayer:ad:begin', function(t) { @@ -44,30 +145,84 @@ }); }; + /** + * Adding a new volume control as a numerical input form + */ + var addVolumeNumericInput = function(){ + console.log('[DIUserscript] addVolumeNumericInput'); + + try{ + var $inputVolume = jQuery(''); + $inputVolume + .val(WebPlayer.getVolume()) // Initializing the input value with the current app volume + .on('change', function(e){ + e.stopPropagation(); + // When the user is using the input, we set the app volume corresponding to the value displayed. + WebPlayer.setVolume(jQuery(this).val()); + }) + .on('click', function(e){ + // Prevents the volume to reach 100 each time we change the volume using the input's native arrows, due to where the input is injected into the DOM for convenience. + e.stopPropagation(); + }) + ; + + var $containerCandidates = jQuery('#popup-volume'); // multisite supported: di and the others + if ($containerCandidates <= 0) { + throw {message: 'Cannot find a proper container in the DOM'}; + } + $inputVolume.appendTo($containerCandidates.first().find('.icon-sound')); // This is kind of hacky, but quick and working. + $containerCandidates.first() + .css('width', '210px') + .css('left', '-186px') + ; + + // When the user is changing the volume using the default slider, we adjust the input value with the new volume value. + WebPlayer.event.onVolumeChange(function(e, volume) { + this.val(volume); + }.bind($inputVolume)); + + } catch(err) { + console.error('[DIUserscript] addVolumeNumericInput error: ', err.message); + } + }; + + /** + * Confusing the client internal timers + * No certain this is still useful + */ var redefineTimers = function(){ - console.warn('[DIUserscript] redefineTimers'); + console.log('[DIUserscript] redefineTimers'); + var getTimeRemaining = function() { + console.warn('[DIUserscript] timers getTimeRemaining', 1337); + return 1337; + }; + try{ - di.app.WebplayerApp.Ads.Supervisor.timers.gracePeriod.getTimeRemaining = function() { return 1337; }; - di.app.WebplayerApp.Ads.Supervisor.timers.midroll.getTimeRemaining = function() { return 1337; }; - di.app.WebplayerApp.Ads.Supervisor.timers.session.getTimeRemaining = function() { return 1337; }; + di.app.WebplayerApp.Ads.Supervisor.timers.gracePeriod.getTimeRemaining = getTimeRemaining; + di.app.WebplayerApp.Ads.Supervisor.timers.session.getTimeRemaining = getTimeRemaining; } catch(err) { - console.error('[DIUserscript] redefineTimers error', err.message); + console.error('[DIUserscript] redefineTimers error: ', err.message); } }; + /** + * Overriding parts of the Command bus + */ var redefineCommands = function(){ - console.warn('[DIUserscript] redefineCommands'); + console.log('[DIUserscript] redefineCommands'); - // Go away + // Go away: Confusing the anti-adblock system into thinking we will not enforce its rules defineProp(di.app.commands._wreqrHandlers['adblocker:enforceWall'], 'callback', function () { console.warn('[DIUserscript] adblocker:enforceWall', 'rejected'); return jQuery.Deferred().reject().promise(); }); - }; + /** + * Overriding parts of the Request bus + */ var redefineReqres = function(){ - console.warn('[DIUserscript] redefineReqres'); + console.log('[DIUserscript] redefineReqres'); try { @@ -119,12 +274,13 @@ // Always judging people ... defineProp(di.app.reqres._wreqrHandlers['current_user:isPremium'], 'callback', function () { console.warn('[DIUserscript] current_user:isPremium', true); + // This is only to confuse the DI client, this will not give access to higher audio qualitiy or premium perks, unless if logged in with a real paid premium account from DI. return true; }); // Lets pretend var userType = di.app.reqres.request('current_user:type'); // guest|public|premium - console.warn('[DIUserscript] current_user:type ORIGINAL ', userType); + console.log('[DIUserscript] current_user:type ORIGINAL ', userType); if (userType === 'guest') { defineProp(di.app.reqres._wreqrHandlers['current_user:type'], 'callback', function () { console.warn('[DIUserscript] current_user:type', 'premium'); @@ -133,47 +289,15 @@ } } catch(err) { - console.error('[DIUserscript] redefineReqres error', err.message); + console.error('[DIUserscript] redefineReqres error: ', err.message); } - }; - // jQuery(window.document).ready(function() { - document.addEventListener("DOMContentLoaded", function(event) { - console.warn('[DIUserscript] window.document ready'); - - redefineCommands(); - redefineReqres(); - adSilencer(); - - setTimeout(function(){ - // // Improved webplayer quality (3 = High) (disabled, Free Listeners can change it via their account settings) - // console.warn('[DIUserscript] preferredQuality:set', 3); - // di.app.commands.execute('preferredQuality:set', 3); - // setTimeout(function(){ - // var audioQuality = di.app.reqres.request('audioQualities:selected').attributes; - // di.app.commands.execute('message:success', 'Webplayer audio quality is now: '+audioQuality.name+' '+audioQuality.content_quality.name+' '+audioQuality.content_format.name); - // }, 2000); - - redefineTimers(); - di.app.commands.execute('message:notice', 'DIUserscript is enabled ! enjoy free uninterrupted music'); - }, 2000); - - setInterval(function(){ - // Removing Premium ad display - jQuery('.premium-upsell').remove(); - jQuery('.menu-item.go-premium').remove(); - jQuery('.sidebar-ad-component').remove(); - jQuery('#panel-ad').remove(); - jQuery('.go-premium-cta').remove(); - - // Breaking anti AFK system - try { di.eventbus.trigger('user:active'); } catch(err) { console.error('(DIUserscript) : '+err.message); } - try { di.app.vent.trigger('user:active'); } catch(err) { console.error('(DIUserscript) : '+err.message); } - try { di.app.timedAlerts.stop(); } catch(err) { console.error('(DIUserscript) : '+err.message); } - }, 1000); - - + /** + * Overriding BlockAdBlock to prevent the anti-adblock system from messing with us. Who wants to be forced to eat shit ? + */ + var redefineBlockAdBlock = function(){ + console.log('[DIUserscript] redefineBlockAdBlock'); // Fuck BlockAdBlock var BlockAdBlock = function(options) { @@ -206,6 +330,52 @@ }; }; defineProp(window, 'BlockAdBlock', BlockAdBlock); - }); + }; + + /** + * Executed once, 2s after DOM document is ready + */ + var initTimeout = function(){ + console.log('[DIUserscript] initTimeout'); + + setTimeout(function(){ + // // Improved webplayer quality (3 = High) (disabled, Free Listeners can change it via their account settings) + // console.warn('[DIUserscript] preferredQuality:set', 3); + // di.app.commands.execute('preferredQuality:set', 3); + // setTimeout(function(){ + // var audioQuality = di.app.reqres.request('audioQualities:selected').attributes; + // di.app.commands.execute('message:success', 'Webplayer audio quality is now: '+audioQuality.name+' '+audioQuality.content_quality.name+' '+audioQuality.content_format.name); + // }, 2000); + + redefineTimers(); + + // Self promoting this script in case you want to quickly find the link to send to a friend ;) just comment this next line if you don't want it. + jQuery('').appendTo('#side-nav ul'); + + di.app.commands.execute('message:notice', 'Enjoy free uninterrupted music thanks to DIUserscript !'); + }, 2000); + }; + + /** + * Executed every 1s, removing visual ads and blocking panels, popups, etc... + * Also making sure the anti AFK system stay confused. + */ + var initInterval = function(){ + console.log('[DIUserscript] initInterval'); + + setInterval(function(){ + // Removing Premium ad display + jQuery('.premium-upsell').remove(); + jQuery('.menu-item.go-premium').remove(); + jQuery('.sidebar-ad-component').remove(); + jQuery('#panel-ad').remove(); + jQuery('.go-premium-cta').remove(); + removeHomeHeroPremiumVisualAds(); + + // Breaking anti AFK system + try { di.app.vent.trigger('user:active'); } catch(err) { console.error('[DIUserscript] : '+err.message); } + try { di.app.timedAlerts.stop(); } catch(err) { console.error('[DIUserscript] : '+err.message); } + }, 1000); + }; })(); diff --git a/README.md b/README.md index 364b147..0424078 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,12 @@ This userscript is trying to minimize and block audio and visual advertisement o ## Features -* Preventing **almost all** ads to reach your ears by bypassing them. -* Removing some over-intrusive "premium" ad menu entry, popup and panel on all pages. +* Preventing **all** automated ads to reach your ears, bypassing them. +* Removing most over-intrusive "premium" ad menu entry, popup, banners and panels on all pages. (If you want to upgrade You still can click on "Free Listener" while logged in) * Breaking the anti-AFK system, so you can let the music run for hours without any human interaction. * Breaking the anti-adblocker system, so you can enjoy the music with AdBlock or AdblockPlus enabled. * Auto mute whenever an ad is playing, auto unmute when finished (just in case !). +* Added a numeric volume input control for precise tuning. ## Supported sites @@ -22,13 +23,12 @@ This userscript is trying to minimize and block audio and visual advertisement o Since I am only using di.fm, please consider helping by [giving feedback](https://github.com/lethak/digitally_imported_userscript/issues/5) for the other sites (what is not working, almost working, etc...) - ## Installation ### First time -Load this [userscript](https://github.com/lethak/digitally_imported_userscript/raw/master/DIU.user.js) with [greasemonkey <= 3.x](https://addons.mozilla.org/en-gb/firefox/addon/greasemonkey/) or [tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo) (recommended) browser extension +Load this [userscript](https://github.com/lethak/digitally_imported_userscript/raw/master/DIU.user.js) with the recommended [tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo) browser extension. -Please note, the script is not working with Firefox "Quantum" >= 57 & Greasemonkey > 4.x, this is why Tampermonkey is recommended. [more on this here](https://github.com/lethak/digitally_imported_userscript/issues/8) +If you prefer greasemonkey over tampermonkey, please read the Troubleshooting section. Userscript: https://github.com/lethak/digitally_imported_userscript/raw/master/DIU.user.js @@ -44,8 +44,34 @@ Sometimes, chrome and firefox will disable greasemonkey or tampermonkey for some I have not managed to make the script work on mobile or tablet. If you have any clue on how to do it, please create a new issue or submit a pull request. +For [greasemonkey](https://addons.mozilla.org/en-gb/firefox/addon/greasemonkey/) users, the script is having troubles with Firefox since the "Quantum" update (version >= 57), this is why the latest version of [tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo) is now recommended for all browsers. + +Compatibility: + +| - | Firefox < 57 | Firefox Quantum >= 57 | Chrome | +|---|---|---|---| +| Greasemonkey <= 3.x | OK | Broken | OK | +| Tampermonkey <= 3.x | OK | Broken | OK | +| Greasemonkey >= 4.x | - | Broken | ? | +| Tampermonkey >= 4.x | - | OK | OK | + +[more on this here](https://github.com/lethak/digitally_imported_userscript/issues/8) + +## Versioning + +Each public version of the script is released as a tag with the following format: + +> .. + +## Disclaimer + +This user-script is in no way affiliated with DI or associated companies, brands or trademarks, and is provided for private personal use only, without any warranty whatsoever. + + ## Still polluted by ads ? Let me know via the "issue" section of this repository, or submit a pull request if you know how to fix the script. If you can afford it, please consider [becoming premium](https://www.di.fm/premium). You will get access to DI in high quality and can listen from VLC or other players. + + diff --git a/draft.txt b/draft.txt index 2eda0f9..cc2c863 100644 --- a/draft.txt +++ b/draft.txt @@ -1,3 +1,4 @@ +di.app.WebplayerApp.model.setVolume(... // Toying with events ... @@ -5,8 +6,10 @@ di.eventbus.trigger('user:active'); // deprecated di.app.vent.trigger('user:active'); .listenTo(t.vent, "webplayer:playing:change", this.onPlayingChange) + di.app.vent._events + locationChange mobilenav:open on_air_event:upcoming payment:method:removed @@ -17,6 +20,7 @@ di.app.vent._events search:open sidenav:toggle track:purchase + user:active webplayer:ad:begin webplayer:ad:end webplayer:ad:noneAvailable @@ -31,6 +35,7 @@ di.app.vent._events // Logic hub ... +di.app.reqres.request(... di.app.reqres._wreqrHandlers adblocker:detected