1use color_thief::{ColorFormat, get_palette};
2use image::ImageReader;
3use reqwest;
4use std::io::Cursor;
5
6#[derive(Debug, Clone, PartialEq, Default)]
7pub struct Color {
8 pub r: u8,
9 pub g: u8,
10 pub b: u8,
11}
12
13impl Color {
14 pub fn new(r: u8, g: u8, b: u8) -> Self {
15 Self { r, g, b }
16 }
17}
18
19pub async fn get_palette_from_url(url: &str) -> Option<Vec<Color>> {
20 let bytes = if url.starts_with("http") {
21 reqwest::get(url).await.ok()?.bytes().await.ok()?.to_vec()
22 } else {
23 let path = if let Some(stripped) = url.strip_prefix("artwork://local") {
24 let decoded = percent_encoding::percent_decode_str(stripped).decode_utf8_lossy();
25 decoded.to_string()
26 } else {
27 url.to_string()
28 };
29 std::fs::read(path).ok()?
30 };
31
32 let img = ImageReader::new(Cursor::new(bytes))
33 .with_guessed_format()
34 .ok()?
35 .decode()
36 .ok()?;
37
38 let rgb = img.to_rgb8();
39 let pixels = rgb.as_raw();
40
41 let palette = get_palette(pixels, ColorFormat::Rgb, 10, 8).ok()?;
42
43 Some(
44 palette
45 .into_iter()
46 .map(|p| Color::new(p.r, p.g, p.b))
47 .collect(),
48 )
49}
50
51pub fn get_background_style(colors: Option<&[Color]>) -> String {
52 if let Some(colors) = colors
53 && !colors.is_empty()
54 {
55 let bg_color = &colors[0];
56 let mut bg_image_parts = Vec::new();
57 let positions = [
58 "0% 0%",
59 "100% 0%",
60 "100% 100%",
61 "0% 100%",
62 "50% 50%",
63 "25% 0%",
64 "75% 100%",
65 ];
66 for (i, c) in colors.iter().skip(1).enumerate().take(positions.len()) {
67 let pos = positions[i];
68 bg_image_parts.push(format!(
69 "radial-gradient(circle at {}, rgba({}, {}, {}, 0.8) 0%, transparent 80%)",
70 pos, c.r, c.g, c.b
71 ));
72 }
73
74 if bg_image_parts.is_empty() {
75 return format!(
76 "background-color: rgb({}, {}, {}); background-image: none;",
77 bg_color.r, bg_color.g, bg_color.b
78 );
79 } else {
80 return format!(
81 "background-color: rgb({}, {}, {}); background-image: {};",
82 bg_color.r,
83 bg_color.g,
84 bg_color.b,
85 bg_image_parts.join(", ")
86 );
87 }
88 }
89 "background-color: var(--color-black); background-image: none;".to_string()
90}