fix: stop the YouTube covers coming out blurry and cropped

The ytimg variants are 16:9, so picking one by width handed a square tile far
fewer pixels than it needed: a 96px cover got the 120x90 file and kept 90px
after the crop. They are chosen by their short side now, the album art is
preferred over the video frame when the search returns both, and the display
sizes account for the tiles being up to 180px on a 2x screen.
This commit is contained in:
2026-07-20 19:05:44 +02:00
parent 3cbf64a191
commit ff6103cd58
4 changed files with 17 additions and 11 deletions
@@ -167,8 +167,11 @@ namespace Musify.Infrastructure.Services
{ {
var ordered = thumbnails.OrderBy(thumbnail => thumbnail.Width).ToList(); var ordered = thumbnails.OrderBy(thumbnail => thumbnail.Width).ToList();
var chosen = ordered.FirstOrDefault(thumbnail => thumbnail.Width >= configuration.ThumbnailSize) var square = ordered.Where(thumbnail => YouTubeThumbnail.IsSquare(thumbnail.Url)).ToList();
?? ordered.LastOrDefault(); var candidates = square.Count > 0 ? square : ordered;
var chosen = candidates.FirstOrDefault(thumbnail => thumbnail.Width >= configuration.ThumbnailSize)
?? candidates.LastOrDefault();
return chosen is null return chosen is null
? string.Empty ? string.Empty
@@ -2,6 +2,9 @@ namespace Musify.Infrastructure.Services
{ {
public static class YouTubeThumbnail public static class YouTubeThumbnail
{ {
public static bool IsSquare(string url) =>
url.Contains("googleusercontent.com", StringComparison.OrdinalIgnoreCase);
public static string WithSize(string url, int size) public static string WithSize(string url, int size)
{ {
if (string.IsNullOrEmpty(url)) if (string.IsNullOrEmpty(url))
@@ -40,9 +43,9 @@ namespace Musify.Infrastructure.Services
private static string YtImgFileName(int size) => size switch private static string YtImgFileName(int size) => size switch
{ {
<= 120 => "default.jpg", <= 90 => "default.jpg",
<= 320 => "mqdefault.jpg", <= 180 => "mqdefault.jpg",
<= 480 => "hqdefault.jpg", <= 360 => "hqdefault.jpg",
_ => "maxresdefault.jpg" _ => "maxresdefault.jpg"
}; };
+3 -3
View File
@@ -55,9 +55,9 @@ export function resizeThumbnailUrl(url: URL, size: number): string {
} }
function ytImgFileName(size: number): string { function ytImgFileName(size: number): string {
if (size <= 120) return 'default.jpg'; if (size <= 90) return 'default.jpg';
if (size <= 320) return 'mqdefault.jpg'; if (size <= 180) return 'mqdefault.jpg';
if (size <= 480) return 'hqdefault.jpg'; if (size <= 360) return 'hqdefault.jpg';
return 'maxresdefault.jpg'; return 'maxresdefault.jpg';
} }
+3 -3
View File
@@ -1,7 +1,7 @@
export const THUMBNAIL_SIZES = { export const THUMBNAIL_SIZES = {
small: 96, small: 128,
medium: 160, medium: 192,
large: 320 large: 360
} as const; } as const;
export type ThumbnailSize = keyof typeof THUMBNAIL_SIZES; export type ThumbnailSize = keyof typeof THUMBNAIL_SIZES;