// ==UserScript== // @name Trakt.tv | Custom Profile Header Image // @description A custom profile image for free users. Like the vip feature, except this one only works locally. Uses the native set/reset buttons and changes the dashboard + settings background as well. See README for details. // @version 1.1.8 // @namespace https://github.com/Fenn3c401 // @author Fenn3c401 // @license GPL-3.0-or-later // @homepageURL https://github.com/Fenn3c401/Trakt.tv-Userscript-Collection#readme // @supportURL https://github.com/Fenn3c401/Trakt.tv-Userscript-Collection/issues // @updateURL https://update.greasyfork.org/scripts/557303.meta.js // @downloadURL https://raw.githubusercontent.com/Fenn3c401/Trakt.tv-Userscript-Collection/main/userscripts/dist/2dz6ub1t.user.js // @icon https://trakt.tv/assets/logos/logomark.square.gradient-b644b16c38ff775861b4b1f58c1230f6a097a2466ab33ae00445a505c33fcb91.svg // @match https://trakt.tv/* // @match https://classic.trakt.tv/* // @run-at document-start // @grant unsafeWindow // @grant GM_info // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // ==/UserScript== /* global moduleName */ 'use strict'; let $; const logger = { _defaults: { title: (typeof moduleName !== 'undefined' ? moduleName : GM_info.script.name).replace('Trakt.tv', 'Userscript'), toast: true, toastrOpt: { positionClass: 'toast-top-right', timeOut: 10000, progressBar: true }, toastrStyles: '#toast-container#toast-container a { color: #fff !important; border-bottom: dotted 1px #fff; }', }, _print(fnConsole, fnToastr, msg = '', opt = {}) { const { title = this._defaults.title, toast = this._defaults.toast, toastrOpt, toastrStyles = '', consoleStyles = '', data } = opt, fullToastrMsg = `${msg}${data !== undefined ? ' See console for details.' : ''}`; console[fnConsole](`%c${title}: ${msg}`, consoleStyles, ...(data !== undefined ? [data] : [])); if (toast) unsafeWindow.toastr?.[fnToastr](fullToastrMsg, title, { ...this._defaults.toastrOpt, ...toastrOpt }); }, info(msg, opt) { this._print('info', 'info', msg, opt) }, success(msg, opt) { this._print('info', 'success', msg, { consoleStyles: 'color:#00c853;', ...opt }) }, warning(msg, opt) { this._print('warn', 'warning', msg, opt) }, error(msg, opt) { this._print('error', 'error', msg, opt) }, }; const gmStorage = { ...(GM_getValue('customProfileImage')) }; GM_setValue('customProfileImage', gmStorage); let styles = addStyles(); window.addEventListener('turbo:load', () => { if (!/^\/(shows|movies|users|dashboard|settings|oauth\/(authorized_)?applications)/.test(location.pathname)) return; $ ??= unsafeWindow.jQuery; if (!$) return; const $coverWrapper = $('body.is-self #cover-wrapper'), $btnSetProfileImage = $('body.is-self #btn-set-profile-image'), $fullScreenshot = $('body:is(.shows, .movies) #summary-wrapper > .full-screenshot'); if (gmStorage.imgUrl && $coverWrapper.length && $btnSetProfileImage.length) addUserPageElems($coverWrapper, $btnSetProfileImage); if ($fullScreenshot.length) { if ($fullScreenshot.attr('style')) addTitlePageElems($fullScreenshot); else { new MutationObserver((_muts, mutObs) => { mutObs.disconnect(); addTitlePageElems($fullScreenshot); }).observe($fullScreenshot[0], { attributeFilter: ['style'] }); // native logic for selection of bg img (fanart vs screenshot) is quite complex } } }); function addUserPageElems($coverWrapper, $btnSetProfileImage) { if ($coverWrapper.has('a.selected:contains("Profile")').length) { $coverWrapper.removeClass('slim') .find('> .poster-bg-wrapper').removeClass('poster-bg-wrapper').addClass('shade'); if (!$coverWrapper.find('> #watching-now-wrapper').length) { $coverWrapper.find('> .container').before( `
` ); } } else { $coverWrapper.find('> .poster-bg-wrapper').removeClass('poster-bg-wrapper').addClass('shadow-full-width'); } $btnSetProfileImage.popover('destroy').popover({ trigger: 'manual', container: 'body', placement: 'bottom', html: true, template: `