ncmc_lib 0.4.0

convert ncm to mp3 or flac ...
Documentation
ncmc_lib-0.4.0 has been yanked.

About

The lib for the tool to convert ncm file to mp3/flac/...

网易云音乐的ncm文件转换工具ncm_c的库。

Usage

use ncmc_lib::NcmFile;

let ncm = NcmFile::open("path/to/your.ncm").unwrap();
ncm.save().unwrap();

Encoding

The pipeline is reversible: [encode::encode_ncm] wraps a plaintext audio stream back into a valid .ncm container. The audio body is XOR-based and therefore symmetric — the same keystream both decrypts and encrypts.

use ncmc_lib::{NcmFile, encode::encode_ncm};
use std::io::Read;

// Decode an existing file to plaintext audio + metadata...
let mut ncm = NcmFile::open("in.ncm").unwrap();
let mut audio = Vec::new();
ncm.read_to_end(&mut audio).unwrap();
let meta = NcmFile::open("in.ncm").unwrap().into_parts().0;

// ...then re-encode. `key_data` is the raw content key: any non-empty slice
// yields a decodable file (the NetEase client uses a per-file random key).
let key_data: Vec<u8> = (0..=200).collect();
let out = std::fs::File::create("out.ncm").unwrap();
encode_ncm(&meta, &key_data, std::io::Cursor::new(audio), out).unwrap();

Async streaming (stream feature)

With the stream feature, stream::NcmStream decodes a .ncm as it arrives over the network: it parses the header off the front of the stream, then is itself a futures::Stream yielding the decrypted audio body. The adapters are plain poll-based wrappers over the futures traits (no async fn in traits), so they are executor-agnostic — tokio, smol, async-std all work.

use futures::StreamExt;
use ncmc_lib::stream::NcmStream;
use std::io;

let resp = reqwest::get("https://example.com/song.ncm").await?;
// Map the stream's error into `io::Error`, which `NcmStream` requires.
let bytes = resp.bytes_stream().map_err(io::Error::other);

let mut ncm = NcmStream::open(bytes).await?;
println!("decoding {}", ncm.meta().music_name);

let mut file = tokio::fs::File::create("song.mp3").await?;
while let Some(chunk) = ncm.next().await {
    tokio::io::AsyncWriteExt::write_all(&mut file, &chunk?).await?;
}

Bridging to tokio

NcmStream is a futures::Stream, which tokio consumes directly (as above). For the reverse direction — turning our decoded futures::Stream<Item = Bytes> into a tokio::io::AsyncRead — wrap it with tokio_util::io::StreamReader; to feed a tokio AsyncRead source into NcmStream::open, turn it into a stream first with tokio_util::io::ReaderStream:

use tokio_util::io::{ReaderStream, StreamReader};

// tokio AsyncRead source -> stream -> decoded NcmStream
let src = tokio::fs::File::open("song.ncm").await?;
let ncm = NcmStream::open(ReaderStream::new(src)).await?;

// decoded futures::Stream -> tokio AsyncRead
let mut reader = StreamReader::new(ncm);
tokio::io::copy(&mut reader, &mut tokio::io::stdout()).await?;

For lower-level composition, stream::DecryptStream transforms a body-only chunk stream when you already hold a [Key] (from [NcmFile::into_cipher]), and stream::EncryptSink is the encoding dual.

features

  • cover_download: provide with_cover method to download cover image from internet if not contained in ncm file.
  • stream: async, futures-based stream::NcmStream / stream::DecryptStream / stream::EncryptSink adapters for decoding and encoding over a futures::Stream.

Acknowledgement