ncm_core 0.2.11

core decoder for ncmc
Documentation
use crate::{
    audio::{Audio, Type as AudioType},
    image::Image,
    key::{decrypt_key, decrypt_meta},
};
use anyhow::{ensure, Ok, Result};
use base64::{engine::general_purpose::STANDARD as base64, Engine};
use std::{io::Read, vec};

#[derive(Debug)]
pub struct Decoder<R>
where
    R: Read,
{
    pub key: Vec<u8>,
    pub comment: Vec<u8>,
    pub meta: Vec<u8>,
    pub image: Option<Image>,
    pub audio: Audio<R>,
}

impl<R> Decoder<R>
where
    R: Read,
{
    pub fn decode(mut input: R) -> Result<Self> {
        let mut buffer = [0; 10];
        input.read_exact(&mut buffer)?;

        ensure!(&buffer[..8] == b"CTENFDAM", "CTENFDAM file header mismatch");

        let key = {
            let (mut key, _) = Self::read_frame(&mut input)?;

            let key = decrypt_key(&mut key)?;

            ensure!(&key[..17] == b"neteasecloudmusic", "Invalid ncm key");

            key[17..].to_vec()
        };

        let comment = {
            let (mut comment, _) = Self::read_frame(&mut input)?;
            if !comment.is_empty() {
                comment.iter_mut().for_each(|byte| *byte ^= 99);

                ensure!(&comment[..22] == b"163 key(Don't modify):", "Invalid comment");
            }

            comment
        };

        let meta = if !comment.is_empty() {
            let meta = &comment[22..];
            let mut meta = base64.decode(meta)?;

            let meta = decrypt_meta(&mut meta)?;

            ensure!(&meta[..6] == b"music:", "Invalid meta");
            meta[6..].to_vec()
        } else {
            vec![]
        };

        Self::skip(&mut input, 5)?;

        let image = {
            let offset = Self::read_len(&mut input)?;

            let (image, img_len) = Self::read_frame(&mut input)?;

            if offset > img_len {
                Self::skip(&mut input, (offset - img_len) as usize)?;
            }

            if img_len > 0 {
                Some(image.into())
            } else {
                None
            }
        };

        let audio = Audio::try_new(input, &key)?;

        Ok(Self { key, comment, meta, image, audio })
    }

    pub fn audio_type(&self) -> AudioType {
        self.audio.r#type()
    }

    pub fn ext(&self) -> String {
        self.audio.ext()
    }

    fn read_frame(input: &mut R) -> Result<(Vec<u8>, u32)> {
        let len = Self::read_len(input)?;
        if len > 0 {
            let mut data = vec![0; len.try_into()?];
            input.read_exact(&mut data)?;
            Ok((data, len))
        } else {
            Ok((vec![], 0))
        }
    }

    fn read_len(input: &mut R) -> Result<u32> {
        let mut buffer = [0; 4];
        input.read_exact(&mut buffer)?;
        Ok(u32::from_le_bytes(buffer))
    }

    fn skip(input: &mut R, i: usize) -> Result<()> {
        let mut buffer = vec![0; i];
        input.read_exact(&mut buffer)?;

        Ok(())
    }
}