aboutsummaryrefslogtreecommitdiff
path: root/.config/awesome/quarrel/native/src/mpd.rs
blob: 08c9e4225407db3d2b9b867d5aba9938645f1f30 (plain)
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
use std::{
    ffi::c_void,
    fs::File,
    net::TcpStream,
    path::PathBuf,
    sync::mpsc::channel,
};

use dirs::home_dir;
use gdk_pixbuf::{
    ffi::GdkPixbuf,
    glib::translate::IntoGlibPtr,
    traits::PixbufLoaderExt,
    Pixbuf,
    PixbufLoader,
};
use mlua::{
    prelude::*,
    LuaSerdeExt,
};
use mpd::Client;
use once_cell::sync::Lazy;
use symphonia::{
    core::{
        formats::FormatOptions,
        io::{
            MediaSourceStream,
            MediaSourceStreamOptions,
        },
        meta::MetadataOptions,
        probe::Hint,
    },
    default::get_probe,
};

static MPD_MUSIC_PATH: Lazy<PathBuf> = Lazy::new(|| {
    [
        home_dir().expect("home directory should be set"),
        "Music".into(),
    ]
    .iter()
    .collect()
});

static COVER_FORMATS: [&str; 2] = ["png", "jpg"];

pub struct Connection(Client<TcpStream>);

impl LuaUserData for Connection {
    fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
        methods.add_method_mut("status", |lua, this: &mut Connection, (): ()| {
            lua.to_value(&this.0.status().map_err(LuaError::external)?)
        });

        methods.add_method_mut("song", |lua, this: &mut Connection, (): ()| {
            Ok(
                if let Some(song) = this.0.currentsong().map_err(LuaError::external)? {
                    lua.to_value(&song)?
                } else {
                    LuaNil
                },
            )
        });

        methods.add_method(
            "get_cover_pixbuf",
            |_, _: &Connection, file: String| {
                let song_path = MPD_MUSIC_PATH.join(file);
                let mut has_external_cover = false;
                let mut cover_path = PathBuf::new();

                for format in COVER_FORMATS {
                    let cover = song_path
                        .parent()
                        .unwrap()
                        .to_owned()
                        .join(format!("cover.{}", format));
                    if cover.exists() {
                        has_external_cover = cover.exists();
                        cover_path = cover;
                        break;
                    }
                }

                let mss = MediaSourceStream::new(
                    Box::new(File::open(song_path)?),
                    MediaSourceStreamOptions::default(),
                );

                let mut probed = get_probe()
                    .format(
                        &Hint::default(),
                        mss,
                        &FormatOptions::default(),
                        &MetadataOptions::default(),
                    )
                    .map_err(LuaError::external)?;

                let visuals;

                if let Some(metadata) = probed.format.metadata().skip_to_latest() {
                    visuals = metadata.visuals();
                    if visuals.is_empty() && has_external_cover {
                        let pixbuf = Pixbuf::from_file(cover_path).map_err(LuaError::external)?;

                        return Ok((
                            Some(LuaLightUserData(unsafe {
                                <Pixbuf as IntoGlibPtr<*mut GdkPixbuf>>::into_glib_ptr(pixbuf)
                                    .cast::<c_void>()
                            })),
                            Some(true),
                        ));
                    }

                    let loader = PixbufLoader::new();
                    loader
                        .write(visuals.first().unwrap().data.as_ref())
                        .map_err(LuaError::external)?;
                    loader.close().map_err(LuaError::external)?;

                    return Ok((
                        Some(LuaLightUserData(unsafe {
                            <Pixbuf as IntoGlibPtr<*mut GdkPixbuf>>::into_glib_ptr(
                                loader.pixbuf().expect("Pixbuf should be initialized"),
                            )
                            .cast::<c_void>()
                        })),
                        Some(false),
                    ));
                }

                Ok((None, None))
            },
        );
    }
}

pub fn init(_: &Lua, _: ()) -> LuaResult<Connection> {
    Ok(Connection(
        Client::connect("localhost:6600").map_err(LuaError::external)?,
    ))
}