-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathEnhanceYouTubeProfilePictures.user.js
212 lines (191 loc) · 6.22 KB
/
EnhanceYouTubeProfilePictures.user.js
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
// ==UserScript==
// @name Enhance YouTube Profile Pictures (HD Version with Caching)
// @namespace https://github.com/Nick2bad4u/UserStyles
// @version 5.2
// @description Enlarges YouTube profile pictures on mouse over, shows HD version, Caches HD images for faster display using localStorage caching. Enlarges profile picture when a creator hearts a comment.
// @author Nick2bad4u
// @match https://www.youtube.com/*
// @grant none
// @updateURL https://github.com/Nick2bad4u/UserStyles/raw/refs/heads/main/EnhanceYouTubeProfilePictures.user.js
// @downloadURL https://github.com/Nick2bad4u/UserStyles/raw/refs/heads/main/EnhanceYouTubeProfilePictures.user.js
// @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @license UnLicense
// @tag youtube
// ==/UserScript==
(function () {
'use strict';
let debounceTimeout;
const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
const preloadedImages = new Map();
// Load cache from localStorage
function loadCache() {
const cache = JSON.parse(
localStorage.getItem('profilePicCache') ||
'{}',
);
const now = Date.now();
// Clear out expired cache entries
Object.keys(cache).forEach((key) => {
if (
now - cache[key].timestamp >
CACHE_TTL_MS
) {
delete cache[key]; // Remove expired entry
}
});
localStorage.setItem(
'profilePicCache',
JSON.stringify(cache),
); // Update cache after removing expired entries
return cache;
}
// Save cache to localStorage
function saveCache(cache) {
localStorage.setItem(
'profilePicCache',
JSON.stringify(cache),
);
}
let cache = loadCache(); // Load the cache once when the script runs
// Preload HD image
function preloadHDImage(src) {
const hdSrc = src.replace(
/=s(32|88|48)-c/,
'=s800-c',
); // Adjust as needed for HD
if (!preloadedImages.has(hdSrc)) {
if (cache[hdSrc]) {
// If in persistent cache, load directly from cache
preloadedImages.set(
hdSrc,
cache[hdSrc].url,
);
} else {
// Preload HD image and store in cache
const img = new Image();
img.src = hdSrc;
preloadedImages.set(hdSrc, hdSrc); // Store in memory
cache[hdSrc] = {
url: hdSrc,
timestamp: Date.now(),
}; // Cache with timestamp
saveCache(cache); // Save the updated cache
}
}
}
// Function to enlarge profile pictures, show HD image, add black outline, and shift position
function enlargeProfilePic(event) {
clearTimeout(debounceTimeout);
const img = event.target;
// If the image is already enlarged, skip further processing
if (img.dataset.enlarged === 'true') return;
debounceTimeout = setTimeout(() => {
const originalSrc = img.src;
const hdSrc = originalSrc.replace(
/=s(32|88|48)-c/,
'=s800-c',
); // Increase the size to 800px
img.dataset.originalSrc = originalSrc; // Store the original src
img.src =
preloadedImages.get(hdSrc) || hdSrc;
// Get the position of the original image
const rect = img.getBoundingClientRect();
// Set fixed size, position relative to the original image
if (
img.classList.contains(
'h-5.w-5.inline.align-middle.rounded-full.flex-none',
)
) {
img.style.transform =
'scale(6) translateX(20px)';
img.style.transition =
'transform 0.2s ease';
img.style.border = '1px solid black';
img.style.zIndex = '9999';
img.style.position = 'relative';
} else {
img.style.width = '260px'; // Adjust width as needed
img.style.height = '260px'; // Adjust height as needed
img.style.borderRadius = '50%'; // Make the image circular
img.style.position = 'fixed';
img.style.top = `${rect.top - 20}px`; // Adjust vertical position as needed
img.style.left = `${rect.left + 70}px`; // Offset to the right
img.style.border = '2px solid black';
img.style.zIndex = '9999';
}
img.dataset.enlarged = 'true'; // Mark as enlarged to prevent re-enlarging
// Reset after 3 seconds
setTimeout(() => {
resetProfilePic(img);
}, 3000);
}, 100);
}
// Function to reset profile pictures to original size and source
function resetProfilePic(img) {
img.src = img.dataset.originalSrc || img.src; // Restore the original src if it was replaced
img.style.width = ''; // Clear custom width
img.style.height = ''; // Clear custom height
img.style.borderRadius = ''; // Clear circular style
img.style.position = ''; // Reset position to default
img.style.top = ''; // Clear top position
img.style.left = ''; // Clear left position
img.style.border = 'none'; // Remove any border
img.style.zIndex = 'auto'; // Reset z-index
img.style.transform = ''; // Remove any transform applied
delete img.dataset.enlarged; // Remove the enlarged flag
}
// Add event listeners to profile pictures
function addEventListeners() {
const profilePicsChat =
document.querySelectorAll(
'.h-5.w-5.inline.align-middle.rounded-full.flex-none',
);
const profilePicsComments =
document.querySelectorAll(
'.style-scope yt-img-shadow img:not(#avatar-btn > yt-img-shadow img)',
);
const heartedThumbnails =
document.querySelectorAll(
'#creator-heart-button yt-img-shadow img, #creator-heart-button img',
);
profilePicsChat.forEach((pic) => {
preloadHDImage(pic.src); // Preload HD image
pic.addEventListener(
'mouseenter',
enlargeProfilePic,
);
});
profilePicsComments.forEach((pic) => {
preloadHDImage(pic.src); // Preload HD image
pic.addEventListener(
'mouseenter',
enlargeProfilePic,
);
});
heartedThumbnails.forEach((pic) => {
preloadHDImage(pic.src); // Preload HD image
pic.addEventListener(
'mouseenter',
enlargeProfilePic,
); // Add hover event
});
}
// Observe changes in the chat and comments section to dynamically add event listeners
const observer = new MutationObserver(
(mutations) => {
mutations.forEach((mutation) => {
if (mutation.addedNodes.length > 0) {
addEventListeners();
}
});
},
);
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true, // Observe attribute changes
attributeFilter: ['src'], // Only track changes in `src` attribute
});
// Initial call to add event listeners
addEventListeners();
})();