-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathyl9xlca7.user.js
More file actions
140 lines (119 loc) · 5.8 KB
/
yl9xlca7.user.js
File metadata and controls
140 lines (119 loc) · 5.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// ==UserScript==
// @name Trakt.tv | Average Season And Episode Ratings
// @description Shows the average general and personal rating of the seasons of a show and the episodes of a season. You can see the averages for all episodes of a show on its /seasons/all page. See README for details.
// @version 1.0.4
// @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/550070.meta.js
// @downloadURL https://raw.githubusercontent.com/Fenn3c401/Trakt.tv-Userscript-Collection/main/userscripts/dist/yl9xlca7.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
// ==/UserScript==
/* README
> Based on Tusky's [Trakt Average Season Rating](https://greasyfork.org/scripts/30728) userscript.
### General
- The general ratings average is weighted by votes, to account for the inaccurate ratings of unreleased seasons/episodes.
- Specials are always excluded, except on the specials season page.
- Only visible (i.e. not hidden by a filter) items are used for the calculation of the averages and changes to those filters trigger a recalculation.
*/
'use strict';
let $;
const numFormatCompact = new Intl.NumberFormat('en', { notation: 'compact', maximumFractionDigits: 1 });
numFormatCompact.formatTLC = (n) => numFormatCompact.format(n).toLowerCase();
addStyles();
document.addEventListener('turbo:load', () => {
if (!location.pathname.startsWith('/shows/') || location.pathname.includes('/episodes/')) return;
$ ??= unsafeWindow.jQuery;
if (!$) return;
const $grid = $('#seasons-episodes-sortable'),
$summaryUserRating = $('#summary-ratings-wrapper .summary-user-rating'),
$traktRating = $('#summary-ratings-wrapper .trakt-rating');
if (!$grid.length || !$summaryUserRating.length || !$traktRating.length) return;
const avgRatings = unsafeWindow.userscriptAvgSeasonEpisodeRatings = {};
let items;
$summaryUserRating[0].mutObs = new MutationObserver(() => { // .summary-user-rating mutations occur frequently and are caused by all sorts of things
if (!$summaryUserRating.hasClass('popover-on')) {
updatePersRatingElem($summaryUserRating, avgRatings.personal);
}
});
updatePersRatingElem($summaryUserRating);
updateGenRatingElem($traktRating);
const filterSpecials = !location.pathname.endsWith('/seasons/0');
$grid.on('arrangeComplete', () => {
if ($grid.data('isotope')) {
items = $grid.data('isotope').filteredItems.filter((i) => filterSpecials ? i.element.dataset.seasonNumber !== '0' : true);
avgRatings.personal = calcAvgPersRating(items);
avgRatings.general = calcAvgGenRating(items);
updatePersRatingElem($summaryUserRating, avgRatings.personal);
updateGenRatingElem($traktRating, avgRatings.general);
}
});
$(document).off('ajaxSuccess.userscript32985').on('ajaxSuccess.userscript32985', (_evt, _xhr, opt) => {
if (items && /\/ratings\/(seasons|episodes)\.json$|\/rate/.test(opt.url)) { // title was (un)rated OR cached personal ratings (and .corner-ratings) were updated
avgRatings.personal = calcAvgPersRating(items);
updatePersRatingElem($summaryUserRating, avgRatings.personal);
}
});
}, { capture: true });
function calcAvgPersRating(items) {
const persRatings = items.map((i) => +$(i.element).find('.corner-rating > .text').text()).filter(Boolean);
return {
average: persRatings.length ? persRatings.reduce((acc, persRating) => acc + persRating, 0) / persRatings.length : undefined,
votes: persRatings.length,
};
}
function calcAvgGenRating(items) {
const genRatingsVotesSum = items.reduce((acc, i) => acc + i.sortData.votes, 0);
return {
average: genRatingsVotesSum ? items.reduce((acc, i) => acc + (i.sortData.percentage * (i.sortData.votes / genRatingsVotesSum)), 0) : undefined,
votes: genRatingsVotesSum,
};
}
function updatePersRatingElem($summaryUserRating, avgPersRating) {
$summaryUserRating[0].mutObs.disconnect();
$summaryUserRating
.find('.rating')
.each(function() {
const rating = $(this).parent().prev().attr('class').match(/rating-(\d+)/)?.[1];
if (rating) $(this).html(`${rating}<div class="votes">${unsafeWindow.ratingsText?.[rating] ?? ''}</div>`);
});
$summaryUserRating
.find('.number > .votes')
.removeClass('alt')
.text(`avg: ${avgPersRating?.average ? `${avgPersRating.average.toFixed(1)}` : '--'} ` +
`(${avgPersRating?.votes !== undefined ? numFormatCompact.formatTLC(avgPersRating.votes) : '--'} v.)`);
$summaryUserRating[0].mutObs.observe($summaryUserRating[0], { subtree: true, childList: true });
}
function updateGenRatingElem($traktRating, avgGenRating) {
if (!$traktRating.has('.rating .votes').length) {
$traktRating
.find('.votes')
.clone()
.appendTo($traktRating.find('.rating'))
.text((_i, text) => `(${text.match(/^.*? v/)?.[0] ?? '0 v'}.)`);
}
$traktRating
.find('.number > .votes')
.text(`avg: ${avgGenRating?.average ? `${Math.round(avgGenRating.average)}` : '--'}% ` +
`(${avgGenRating?.votes !== undefined ? numFormatCompact.formatTLC(avgGenRating.votes) : '--'} v.)`);
}
function addStyles() {
GM_addStyle(`
#summary-ratings-wrapper .ratings .rating {
display: flex !important;
justify-content: space-between;
align-items: center;
}
#summary-ratings-wrapper .ratings .rating .votes {
margin-left: 7px !important;
color: #fff !important;
}
`);
}