// ==UserScript==
// @name Trakt.tv | Bug Fixes and Optimizations
// @description A large collection of bug fixes and optimizations for trakt.tv. Organized into sections with comments detailing what specific issues are being addressed. See README for details.
// @version 0.7.1
// @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://raw.githubusercontent.com/Fenn3c401/Trakt.tv-Userscript-Collection/main/userscripts/meta/brzmp0a9.meta.js
// @downloadURL https://raw.githubusercontent.com/Fenn3c401/Trakt.tv-Userscript-Collection/main/userscripts/dist/brzmp0a9.min.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_addStyle
// @grant GM_openInTab
// ==/UserScript==
/* README
### Hotkeys and Gestures
- ***[CUSTOM]*** `alt + 1/2/3/4/5/6/7`: change header-search-category, 1 for "Shows & Movies", 2 for "Shows", ..., 7 for "Users", also expands header-search if collapsed
- ***[CUSTOM]*** `swipe in from left edge`: display title sidebar on mobile devices
- `meta(win)/ctrl + left click`: open in new tab instead of redirect (applies to header search results + "view watched history" button on title summary pages)
- `/`: expand header-search
- `w`: show filter-by-streaming-services modal
- `t`: show filter-by-terms modal
- `a`: toggle advanced-filters
- `m`: toggle manage-list mode (with item move, delete etc.)
- `r`: toggle reorder-lists mode (change list-rank on /lists page)
- `esc`: collapse header-search, hide popover, hide modal (check-in, watch-now, filter-by-terms)
- `enter`: redirect to selected header-search result, submit (advanced filters selection, date-time-picker input etc.)
- `ctrl + enter`: save note, submit comment
- `arrow-left/right OR p/n OR swipe right/left on fanart`: page navigation (e.g. prev/next episode, prev/next results page)
- `arrow-up/down`: header-search results navigation
### Filter-By-Terms Regex
The filter-by-terms (called "Filter by Title") function interprets the input as a case-insensitive regular expression, if filering is done client-side with isotope,
which is limited to places where there's no need for pagination (/lists, /seasons and /people pages). Intriguingly the /progress page, despite having pagination and
therefore relying on server-side filtering, does in fact allow for using regular expressions, though from my testing this seems to be the only exception.
The input is matched against: list title and description for /lists pages, episode title for /seasons pages, title and character name for /people pages, episode and show title for /progress pages.
*/
"use strict";GM_addStyle(`
#info-wrapper .season-links .links {
overflow-x: auto;
scrollbar-width: thin;
scrollbar-color: transparent transparent;
transition: scrollbar-color 0.2s;
width: revert !important;
}
#info-wrapper .season-links .links:hover {
scrollbar-color: rgb(102 102 102 / 0.4) transparent;
}
#info-wrapper .season-links .links > ul {
width: max-content !important;
}
`),(e=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e())(()=>{const e=Object.getOwnPropertyDescriptor(unsafeWindow.jQuery.fn,"mCustomScrollbar");e.value=function(t){return this},Object.defineProperty(unsafeWindow.jQuery.fn,"mCustomScrollbar",e)}),document.addEventListener("turbo:load",()=>{document.querySelector("#info-wrapper .season-links .links .selected")?.scrollIntoView({block:"nearest",inline:"start"})},{capture:!0}),(e=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e())(()=>{const e=Object.getOwnPropertyDescriptor(unsafeWindow.jQuery.fn,"tooltip"),t=e.value;e.value=function(r){return r?.container&&this.closest(".popover, #ondeck-wrapper, #progress-grid-wrapper").length&&delete r.container,t.apply(this,arguments)},Object.defineProperty(unsafeWindow.jQuery.fn,"tooltip",e)}),GM_addStyle(`
@media (width <= 767px) {
#info-wrapper .sticky-wrapper {
display: block !important;
}
#info-wrapper .sidebar {
position: fixed;
top: 0 !important;
left: 0;
z-index: 20;
width: 40%;
padding: calc(10px + var(--header-height)) 10px 0;
height: 100%;
background-color: rgb(29 29 29 / 96%);
overflow-y: auto;
transform: translateX(-100%);
transition: transform 0.3s;
margin: revert !important;
}
#info-wrapper.with-mobile-sidebar .sidebar {
transform: translateX(0);
}
}
`),window.addEventListener("turbo:load",()=>{const e=unsafeWindow.jQuery("body.touch-device #info-wrapper:has(.sidebar)");e.swipe({excludedElements:"#summary-ratings-wrapper .stats, #info-wrapper .season-links .links, #actors .posters",swipeRight:(t,r,a,n,o,i)=>i[0].start.x<50&&e.addClass("with-mobile-sidebar"),swipeLeft:(t,r,a,n,o,i)=>e.removeClass("with-mobile-sidebar")})}),window.addEventListener("turbo:load",()=>{document.querySelectorAll("#header-search-type .dropdown-menu li:has(~ .divider) a").forEach((e,t)=>{unsafeWindow.Mousetrap.bind(`alt+${t+1}`,()=>e.click()),unsafeWindow.Mousetrap(document.getElementById("header-search-query")).bind(`alt+${t+1}`,()=>e.click())})});const optimizedRenderReadmore=()=>{unsafeWindow.jQuery('.readmore:not([id^="rmjs-"])').filter((t,r)=>unsafeWindow.jQuery(r).height()>350).readmore({embedCSS:!1,collapsedHeight:300,speed:200,moreLink:'Read more...',lessLink:'Read less...',afterToggle:(t,r,a)=>r.closest("#sortable-grid").length&&unsafeWindow.$grid?.isotope()}),requestAnimationFrame(()=>unsafeWindow.$grid?.isotope())};Object.defineProperty(unsafeWindow,"renderReadmore",{get:()=>optimizedRenderReadmore,set:()=>{},configurable:!0}),GM_addStyle(`
.personal-list .list-description {
overflow-wrap: anywhere;
}
`),(e=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e())(()=>{const e=Object.getOwnPropertyDescriptor(unsafeWindow.jQuery.fn,"chosen"),t=e.value;e.value=function(r){return this.attr("id")==="filter-network_ids"&&(r.max_shown_results=200),t.apply(this,arguments)},Object.defineProperty(unsafeWindow.jQuery.fn,"chosen",e)}),(e=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e())(()=>{const e=unsafeWindow.jQuery;e&&e(document).on("ajaxSend",(t,r,a)=>{if(/\/lists\/[\d]+\/like/.test(a.url)){const n=new URLSearchParams(a.data).get("trakt_id"),o=e(`[data-list-id="${n}"] > .like .count-number`),i=o.text(),s=a.url.includes("/remove");e(document).one("ajaxSuccess",(d,l,p)=>{a.url===p.url&&o.text(unsafeWindow.numeral(i)[s?"subtract":"add"](1).format("0,0"))})}})}),(e=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e())(()=>{const e=unsafeWindow.jQuery;e&&(e(document).on("auxclick",".btn-watch .view-all",function(t){t.preventDefault(),GM_openInTab(location.origin+e(this).attr("data-url"),{insert:!0,setParent:!0})}),e(document).on("mousedown mouseup","#header-search-autocomplete-results .selected",function(t){t.which===2&&!e(t.target).closest("a").length&&(t.type==="mousedown"?t.preventDefault():(unsafeWindow.searchModifierKey=!0,e(this).trigger("click")))}))}),document.addEventListener("keydown",e=>{e.ctrlKey&&e.key==="Enter"&&e.target.closest?.("#header-search-query")&&(e.preventDefault(),e.stopPropagation(),e.target.dispatchEvent(new KeyboardEvent("keydown",{key:"Enter",keyCode:13,metaKey:!0,bubbles:!0,cancelable:!0})))},{capture:!0}),GM_addStyle(`
#activity .users-wrapper {
width: 100%;
padding-bottom: 15px !important;
display: grid;
grid-template-columns: repeat(6, 1fr);
column-gap: 10px;
counter-reset: plusMoreCounter attr(data-count type());
}
#activity .users-wrapper .plus-more {
grid-area: 1 / -2 / 2 / -1;
display: grid;
place-content: center;
position: revert !important;
height: revert !important;
width: revert !important;
}
#activity .users-wrapper .plus-more .text {
position: relative !important;
}
@supports (color: attr(data-color type())) {
#activity .users-wrapper .plus-more .text {
display: none;
}
#activity .users-wrapper .plus-more::after {
content: "+" counter(plusMoreCounter) "\\Amore";
white-space: pre;
line-height: 1;
font-weight: var(--headings-font-weight);
font-family: var(--headings-font-family);
font-size: 16px;
}
}
#activity .users-wrapper .row {
grid-area: 1 / 1 / 2 / -1;
display: grid;
grid-template-columns: subgrid;
row-gap: 10px;
max-height: revert !important;
margin: revert !important;
}
#activity .users-wrapper .row::before,
#activity .users-wrapper .row::after {
content: revert !important;
}
#activity .users-wrapper .row > div {
counter-increment: plusMoreCounter -1;
width: revert !important;
padding: revert !important;
}
#activity .users-wrapper .row > div img {
aspect-ratio: 1; /* for bg while img is loading */
margin-bottom: revert !important;
}
@media (width <= 767px) {
#activity .users-wrapper {
padding-bottom: 10px !important;
}
}
@media (width <= 991px) {
#activity .users-wrapper .row > :nth-child(n + 6) {
display: none;
}
}
@media (991px < width <= 1200px) {
#activity .users-wrapper {
grid-template-columns: repeat(9, 1fr);
}
#activity .users-wrapper .row > :nth-child(n + 9),
#activity .users-wrapper:not(:has(> .row > :nth-child(9))) .plus-more {
display: none;
}
}
@media (width > 1200px) {
#activity .users-wrapper {
grid-template-columns: repeat(12, 1fr);
}
#activity .users-wrapper .row > :nth-child(n + 12),
#activity .users-wrapper:not(:has(> .row > :nth-child(12))) .plus-more {
display: none;
}
}
#activity .users-wrapper .row:has(+ .plus-more[style*="display: none;"]) > div,
#activity .users-wrapper .row:not(:has(+ .plus-more)) > :nth-child(-n + 12) { /* downsizing with 7-12 items (no btn in that case) */
display: block;
}
#actors .posters {
container-type: inline-size;
}
#actors .posters ul {
width: max-content !important;
display: flex;
--gap: 10px;
gap: var(--gap);
}
#actors .posters ul li {
width: calc((100cqi - ((var(--visible-items) - 1) * var(--gap))) / var(--visible-items)) !important;
}
#actors .posters ul li :is(.poster, .titles) {
margin-right: revert !important;
}
@media (width <= 767px) {
#actors .posters ul {
--gap: 0px;
--visible-items: 4;
}
}
@media (767px < width <= 991px) {
#actors .posters ul {
--visible-items: 6;
}
}
@media (991px < width <= 1200px) {
#actors .posters ul {
--visible-items: 8;
}
}
@media (1200px < width) {
#actors .posters ul {
--visible-items: 10;
}
}
.actor-tooltip {
margin-top: 5px;
margin-left: revert !important;
}
`),document.addEventListener("turbo:load",()=>{/^\/people\/[^\/]+$/.test(location.pathname)&&unsafeWindow.jQuery?.("#filter-fade-hide .dropdown-menu li.typer:is(.season, .episode, .person) a.selected").removeClass("selected")},{capture:!0}),window.addEventListener("turbo:load",()=>unsafeWindow.jQuery?.(".feed-icon.csv").off("click")),GM_addStyle(`
@media (767px < width) {
body.comments:has(#read) {
overflow-x: clip !important;
}
body.comments #read > .comment-wrapper > .above-comment::before,
body.comments #read > .comment-wrapper > .above-comment::after {
content: "";
position: absolute;
top: 0;
height: 100%;
background-color: inherit;
width: 100vw;
}
body.comments #read > .comment-wrapper > .above-comment::before {
right: 100%;
}
body.comments #read > .comment-wrapper > .above-comment::after {
left: 100%;
}
}
`),GM_addStyle(`
@media (width <= 767px) {
body.discover .comment-wrapper .comment {
padding-bottom: 30px !important;
}
}
`),GM_addStyle(`
#links-wrapper {
height: 40px !important;
}
#links-wrapper .container {
height: 100% !important;
display: flex !important;
align-items: center;
}
#links-wrapper .container a {
line-height: inherit !important;
}
`),(e=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e())(()=>{unsafeWindow.jQuery?.(document).on("ajaxSuccess",(e,t,r)=>{r.url.endsWith("/dashboard/schedule")&&unsafeWindow.jQuery("#schedule-wrapper .btn-watch-now:not([data-source-counts])").attr("data-source-counts","{}"),/\/(dashboard\/on_deck|progress_item\/watched)\/\d+$/.test(r.url)&&unsafeWindow.posterGridTooltips?.()})}),GM_addStyle(`
.grid-item .actions .list.selected.watchlist .base {
background: #008ada !important;
}
.grid-item .actions .list.selected.personal .base {
background: #0066a0 !important;
}
.grid-item .actions .list.selected.watchlist.personal .base {
background: linear-gradient(90deg, #008ada 50%, #0066a0 50%) !important;
}
`),document.addEventListener("turbo:load",()=>{/^\/people\/[^\/]+$/.test(location.pathname)&&!location.search&&history.replaceState({},document.title,location.pathname+"?sort=popularity,asc")},{capture:!0}),GM_addStyle(`
@supports (color: attr(data-color type())) {
.comment-wrapper[data-user-slug] {
--userslug: attr(data-user-slug);
}
.comment-wrapper[data-user-slug] .user-name :is(.username, .type + strong)::after {
content: " (@" var(--userslug) ")";
}
.comment-wrapper[data-user-slug] .user-name {
max-width: calc(100% - 40px) !important;
}
.comment-wrapper[data-user-slug] .user-name > h4 {
white-space: nowrap;
overflow-x: clip;
text-overflow: ellipsis;
}
}
.comment-wrapper[data-user-slug] .user-name .type + strong {
color: #aaa !important;
}
`),GM_addStyle(`
body {
overflow-x: clip !important;
}
`),GM_addStyle(`
@media (767px < width < 992px) {
.comment-wrapper.list.keep-inline .interactions {
margin-left: revert !important;
}
}
@media (width <= 767px) {
body.watchlist_comments .comment-wrapper.lists {
padding-left: 10px;
}
body.watchlist_comments .comment-wrapper.lists .count-text {
display: none;
}
}
`),GM_addStyle(`
.dark-knight .dropdown-menu a:focus {
background-color: #222 !important;
}
`),GM_addStyle(`
#summary-ratings-wrapper > .container {
padding-top: revert !important;
}
@media (width <= 767px) {
#summary-ratings-wrapper {
border-top: revert !important;
}
#summary-ratings-wrapper .ul-wrapper {
padding: revert !important;
margin-bottom: revert !important;
}
#summary-ratings-wrapper .ul-wrapper ul {
height: 50px;
line-height: 39px;
overflow-x: auto;
scrollbar-width: none;
scrollbar-color: transparent transparent;
transition: scrollbar-color 0.2s;
}
#summary-ratings-wrapper .ul-wrapper ul:hover {
scrollbar-width: thin;
scrollbar-color: rgb(102 102 102 / 0.4) transparent;
}
#summary-ratings-wrapper .ul-wrapper ul.ratings {
padding: 0 10px !important;
border-block: solid 1px #333;
}
#summary-ratings-wrapper .ul-wrapper ul.stats {
margin: 0 10px !important;
padding: 0 !important;
border-top: revert !important;
}
#summary-ratings-wrapper .ul-wrapper ul li {
vertical-align: -37%;
}
}
@media (767px < width) {
#summary-ratings-wrapper .ul-wrapper {
height: 60px;
line-height: 49px;
scrollbar-width: none;
scrollbar-color: transparent transparent;
transition: scrollbar-color 0.2s;
padding-bottom: revert !important;
margin-bottom: revert !important;
}
#summary-ratings-wrapper .ul-wrapper:hover {
scrollbar-width: thin;
scrollbar-color: rgb(102 102 102 / 0.4) transparent;
}
#summary-ratings-wrapper .ul-wrapper li {
vertical-align: -33%;
}
}
`),(e=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e())(()=>{const e=unsafeWindow.jQuery;e&&e(document).on("ajaxSuccess",(t,r,a)=>{if(a.url.endsWith("/rate")){const n=new URLSearchParams(a.data),[o,i,s]=["type","trakt_id","stars"].map(d=>n.get(d));unsafeWindow[o+"s"].ratings[i]=s,unsafeWindow.compressedCache.set(`ratings_${o}s`,unsafeWindow[o+"s"].ratings),unsafeWindow.addOverlays()}else if(a.url.endsWith("/rate/remove")){const n=new URLSearchParams(a.data),o=n.get("type");unsafeWindow.compressedCache.set(`ratings_${o}s`,unsafeWindow[o+"s"].ratings),unsafeWindow.addOverlays()}})}),document.addEventListener("click",e=>{e.target.closest(".toggle-feeds")?(e.stopPropagation(),document.querySelector(".toggle-feeds-wrapper")?.classList.toggle("open")):e.target.closest(".toggle-subnav-options")&&(e.stopPropagation(),document.querySelector(".toggle-subnav-wrapper")?.classList.toggle("open"))},!0),(e=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e())(()=>{["remove","intersection","move","uniq"].forEach(e=>{const t=Object.getOwnPropertyDescriptor(Array.prototype,e);t&&(t.enumerable=!1,Object.defineProperty(Array.prototype,e,t))})}),GM_addStyle(`
body.releases .panel-body {
overflow-x: auto !important;
scrollbar-width: thin;
scrollbar-color: #666 #333;
}
body.releases .panel-body tr :is(th, td):last-of-type {
min-width: revert !important;
}
`),GM_addStyle(`
body.shows :is(#comments, .sidebar .streaming-links) img {
filter: none !important;
}
body.shows #summary-wrapper:has(> .summary .poster.dropped-show) :is(.full-screenshot, .delta, img) {
filter: grayscale(100%) !important;
}
`),GM_addStyle(`
@media (width <= 767px) {
body.season #episodes {
margin-top: 35px !important;
}
}
`);