Log In  


Work in progress Widget/Controller for Spotify using a Rust server

I'm not that good at Lua but i managed to get a OK looking program so far the hardest part was definitely the song cover art processing and drawing, I'm sure there is a better way to do it but this is what i came up with

The performance could be better it freezes for half a second when switching songs or refreshing the song info :(

The rust color conversion code if interested (pls dont laugh at it my cat will cry)

use image::{DynamicImage, GenericImageView, Pixel, Rgb, RgbImage};

const PALETTE: &[Rgb<u8>] = &[
    Rgb([0, 0, 0]),       // #000000
    Rgb([29, 43, 83]),    // #1D2B53
    Rgb([126, 37, 83]),   // #7E2553
    Rgb([0, 135, 81]),    // #008751
    Rgb([171, 82, 54]),   // #AB5236
    Rgb([95, 87, 79]),    // #5F574F
    Rgb([194, 195, 199]), // #C2C3C7
    Rgb([255, 241, 232]), // #FFF1E8
    Rgb([255, 0, 77]),    // #FF004D
    Rgb([255, 163, 0]),   // #FFA300
    Rgb([255, 236, 39]),  // #FFEC27
    Rgb([0, 228, 54]),    // #00E436
    Rgb([41, 173, 255]),  // #29ADFF
    Rgb([131, 118, 156]), // #83769C
    Rgb([255, 119, 168]), // #FF77A8
    Rgb([255, 204, 170]), // #FFCCAA
    Rgb([28, 94, 172]),   // #1C5EAC
    Rgb([0, 165, 161]),   // #00A5A1
    Rgb([117, 78, 151]),  // #754E97
    Rgb([18, 83, 89]),    // #125359
    Rgb([116, 47, 41]),   // #742F29
    Rgb([73, 45, 56]),    // #492D38
    Rgb([162, 136, 121]), // #A28879
    Rgb([255, 172, 197]), // #FFACC5
    Rgb([195, 0, 76]),    // #C3004C
    Rgb([235, 107, 0]),   // #EB6B00
    Rgb([144, 236, 66]),  // #90EC42
    Rgb([0, 178, 81]),    // #00B251
    Rgb([100, 223, 246]), // #64DFF6
    Rgb([189, 154, 223]), // #BD9ADF
    Rgb([228, 13, 171]),  // #E40DAB
    Rgb([255, 133, 109]), // #FF856D
    Rgb([32, 32, 32]),    // #202020
];

/// Compute the Euclidean distance between two RGB colors
fn color_distance(c1: &Rgb<u8>, c2: &Rgb<u8>) -> u32 {
    let r_diff = (c1.0[0] as i32 - c2.0[0] as i32) as f32 * 1.0;
    let g_diff = (c1.0[1] as i32 - c2.0[1] as i32) as f32 * 1.0;
    let b_diff = (c1.0[2] as i32 - c2.0[2] as i32) as f32 * 1.0;

    (r_diff * r_diff + g_diff * g_diff + b_diff * b_diff) as u32
}

/// Find the closest color from the palette
/// Unused but good to have
fn closest_palette_color(color: &Rgb<u8>) -> Rgb<u8> {
    *PALETTE
        .iter()
        .min_by_key(|&palette_color| color_distance(color, &palette_color))
        .unwrap()
}

/// Find the closest color from the palette and return its index
fn closest_palette_index(color: &Rgb<u8>) -> u8 {
    PALETTE
        .iter()
        .enumerate()
        .min_by_key(|&(_, &palette_color)| color_distance(color, &palette_color))
        .map(|(index, _)| index as u8)
        .unwrap()
}

/// Process the image and return a Vec<u8> of palette indices
pub fn process_image_to_indices(img: DynamicImage) -> Vec<u8> {
    let (width, height) = img.dimensions();
    let mut indexed_pixels = Vec::with_capacity((width * height) as usize);

    for y in 0..height {
        for x in 0..width {
            let pixel = img.get_pixel(x, y);
            let index = closest_palette_index(&pixel.to_rgb());
            indexed_pixels.push(index);
        }
    }

    indexed_pixels
}

/// Process the image by mapping colors to the closest ones in the palette
/// Unused but good to have
pub fn process_image(img: DynamicImage) -> RgbImage {
    let (width, height) = img.dimensions();
    let mut new_img: image::ImageBuffer<Rgb<u8>, Vec<u8>> = RgbImage::new(width, height);

    for y in 0..height {
        for x in 0..width {
            let pixel = img.get_pixel(x, y);
            let new_pixel = closest_palette_color(&pixel.to_rgb());
            new_img.put_pixel(x, y, new_pixel);
        }
    }

    new_img
}

And the cover drawing code from lua.


I discovered if the url is the same as it was before the fetch function will return the old information instead of any new information, so i added a random number to the urls, they have no effect on the end results. Just a little interesting tidbit i found out

function fetch_cover()
	local cover = fetch("http://localhost:3030/cover/" ..rnd(999999).. "?url="..trackInfo["album_icons"][3])

	return cover
end

function draw_cover(cover)
	for y=0, 63 do
		for x=0, 63 do
			local index = (y * 64) + x + 1
			local c = cover:byte(index)
			pset(x,y,c)
		end
	end
end

I'll post the server source code at some point if anyone's interested although it's very messy so maybe when i go through and clean it up

Would not have been possible without the work of xietanu!

2



[Please log in to post a comment]