Add files via upload

This commit is contained in:
Savsis 2026-07-07 18:19:47 +05:00 • committed by GitHub
parent 07f2ed525b
commit 6e7f0c1505
37 changed files with 4084 additions and 20 deletions

View file

@ -0,0 +1,46 @@
// a small dependency-free crc32 (used nowhere, just a rust toy sitting
// next to the real python download/convert pipeline).
const POLY: u32 = 0xEDB88320;
fn table() -> [u32; 256] {
let mut table = [0u32; 256];
let mut i = 0u32;
while i < 256 {
let mut c = i;
let mut k = 0;
while k < 8 {
c = if c & 1 != 0 { POLY ^ (c >> 1) } else { c >> 1 };
k += 1;
}
table[i as usize] = c;
i += 1;
}
table
}
pub fn crc32(data: &[u8]) -> u32 {
let table = table();
let mut crc: u32 = 0xFFFFFFFF;
for &byte in data {
let idx = ((crc ^ byte as u32) & 0xFF) as usize;
crc = table[idx] ^ (crc >> 8);
}
crc ^ 0xFFFFFFFF
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn known_vector() {
// crc32("123456789") == 0xCBF43926, the standard check value.
assert_eq!(crc32(b"123456789"), 0xCBF43926);
}
#[test]
fn empty_input() {
assert_eq!(crc32(b""), 0);
}
}

View file

@ -0,0 +1,91 @@
// mirrors the format list from app/backend/converter.py, just as a rust toy.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AudioFormat {
Mp3,
Flac,
Wav,
M4a,
Alac,
Ogg,
Opus,
}
impl AudioFormat {
pub fn all() -> &'static [AudioFormat] {
use AudioFormat::*;
&[Mp3, Flac, Wav, M4a, Alac, Ogg, Opus]
}
pub fn extension(&self) -> &'static str {
match self {
AudioFormat::Mp3 => "mp3",
AudioFormat::Flac => "flac",
AudioFormat::Wav => "wav",
AudioFormat::M4a => "m4a",
AudioFormat::Alac => "alac",
AudioFormat::Ogg => "ogg",
AudioFormat::Opus => "opus",
}
}
pub fn is_lossless(&self) -> bool {
matches!(self, AudioFormat::Flac | AudioFormat::Wav | AudioFormat::Alac)
}
pub fn from_extension(ext: &str) -> Option<AudioFormat> {
AudioFormat::all()
.iter()
.copied()
.find(|f| f.extension().eq_ignore_ascii_case(ext))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ImageFormat {
Jpg,
Png,
Webp,
Bmp,
Gif,
Tiff,
Ico,
}
impl ImageFormat {
pub fn all() -> &'static [ImageFormat] {
use ImageFormat::*;
&[Jpg, Png, Webp, Bmp, Gif, Tiff, Ico]
}
pub fn extension(&self) -> &'static str {
match self {
ImageFormat::Jpg => "jpg",
ImageFormat::Png => "png",
ImageFormat::Webp => "webp",
ImageFormat::Bmp => "bmp",
ImageFormat::Gif => "gif",
ImageFormat::Tiff => "tiff",
ImageFormat::Ico => "ico",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trips_extension() {
for fmt in AudioFormat::all() {
let ext = fmt.extension();
assert_eq!(AudioFormat::from_extension(ext), Some(*fmt));
}
}
#[test]
fn flac_is_lossless() {
assert!(AudioFormat::Flac.is_lossless());
assert!(!AudioFormat::Mp3.is_lossless());
}
}

59
extras/rust/src/main.rs Normal file
View file

@ -0,0 +1,59 @@
// extras/rust — decorative toy crate, not wired into the actual nexdex.space
// build (that's the C++ installer + python app). kept here as a small rust
// playground alongside the app. build with: cargo run --manifest-path Cargo.toml
mod formats;
mod tags;
mod checksum;
mod progress;
mod playlist;
fn banner() -> &'static str {
r#"
_ __ _____ ____| | _____ __
| '_ \ / _ \ \/ / _` |/ _ \ \/ /
| | | | __/> < (_| | __/> <
|_| |_|\___/_/\_\__,_|\___/_/\_\
.space
"#
}
fn main() {
println!("{}", banner());
let track = tags::Tag {
title: "MATADORA (Extended)".to_string(),
artist: "SUKA.".to_string(),
album: None,
year: Some(2026),
track_number: None,
};
println!("{}", track.describe());
for fmt in formats::AudioFormat::all() {
println!(
"{:<6} lossless={}",
fmt.extension(),
fmt.is_lossless()
);
}
let data = b"nexdex.space made by savsis";
println!("crc32({:?}) = {:08x}", String::from_utf8_lossy(data), checksum::crc32(data));
progress::simulate_download("MATADORA (Extended)");
let mut pl = playlist::Playlist::new();
pl.add(playlist::Track {
tag: track.clone(),
path: "downloads/MATADORA (Extended).flac".to_string(),
duration_secs: 222,
});
println!(
"playlist: {} track(s), total {}",
pl.len(),
playlist::Playlist::format_duration(pl.total_duration_secs())
);
println!("made by savsis with <3");
}

111
extras/rust/src/playlist.rs Normal file
View file

@ -0,0 +1,111 @@
// extras/rust — decorative toy: a tiny in-memory playlist model, echoing
// the shape of data/history.json (backend/history.py) but with no I/O.
use crate::tags::Tag;
#[derive(Debug, Clone)]
pub struct Track {
pub tag: Tag,
pub path: String,
pub duration_secs: u32,
}
#[derive(Debug, Default)]
pub struct Playlist {
tracks: Vec<Track>,
}
impl Playlist {
pub fn new() -> Self {
Playlist { tracks: Vec::new() }
}
pub fn add(&mut self, track: Track) {
self.tracks.push(track);
}
pub fn len(&self) -> usize {
self.tracks.len()
}
pub fn is_empty(&self) -> bool {
self.tracks.is_empty()
}
pub fn total_duration_secs(&self) -> u32 {
self.tracks.iter().map(|t| t.duration_secs).sum()
}
pub fn by_artist(&self, artist: &str) -> Vec<&Track> {
self.tracks
.iter()
.filter(|t| t.tag.artist.eq_ignore_ascii_case(artist))
.collect()
}
pub fn sorted_by_title(&self) -> Vec<&Track> {
let mut sorted: Vec<&Track> = self.tracks.iter().collect();
sorted.sort_by(|a, b| a.tag.title.cmp(&b.tag.title));
sorted
}
pub fn longest(&self) -> Option<&Track> {
self.tracks.iter().max_by_key(|t| t.duration_secs)
}
pub fn format_duration(total_secs: u32) -> String {
let hours = total_secs / 3600;
let minutes = (total_secs % 3600) / 60;
let seconds = total_secs % 60;
if hours > 0 {
format!("{}:{:02}:{:02}", hours, minutes, seconds)
} else {
format!("{}:{:02}", minutes, seconds)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tags::TagBuilder;
fn sample_track(title: &str, artist: &str, secs: u32) -> Track {
Track {
tag: TagBuilder::new(title, artist).build(),
path: format!("downloads/{}.flac", title),
duration_secs: secs,
}
}
#[test]
fn total_duration_sums_all_tracks() {
let mut pl = Playlist::new();
pl.add(sample_track("A", "X", 100));
pl.add(sample_track("B", "Y", 200));
assert_eq!(pl.total_duration_secs(), 300);
}
#[test]
fn filters_by_artist_case_insensitively() {
let mut pl = Playlist::new();
pl.add(sample_track("A", "SUKA.", 100));
pl.add(sample_track("B", "suka.", 50));
pl.add(sample_track("C", "Someone Else", 10));
assert_eq!(pl.by_artist("suka.").len(), 2);
}
#[test]
fn finds_longest_track() {
let mut pl = Playlist::new();
pl.add(sample_track("Short", "X", 60));
pl.add(sample_track("Long", "X", 600));
assert_eq!(pl.longest().unwrap().tag.title, "Long");
}
#[test]
fn formats_duration_with_and_without_hours() {
assert_eq!(Playlist::format_duration(65), "1:05");
assert_eq!(Playlist::format_duration(3665), "1:01:05");
}
}

View file

@ -0,0 +1,61 @@
// a toy terminal progress bar, echoing the shape of the progress_hook
// callbacks used by backend/jobs.py — just rendered with print! here
// instead of pushed over the /api/job/<id> polling endpoint.
pub struct ProgressBar {
width: usize,
label: String,
}
impl ProgressBar {
pub fn new(label: impl Into<String>, width: usize) -> Self {
ProgressBar {
width,
label: label.into(),
}
}
pub fn render(&self, percent: u8) -> String {
let percent = percent.min(100) as usize;
let filled = (self.width * percent) / 100;
let empty = self.width - filled;
format!(
"{:<20} [{}{}] {:>3}%",
self.label,
"#".repeat(filled),
"-".repeat(empty),
percent
)
}
}
pub fn simulate_download(label: &str) {
let bar = ProgressBar::new(label, 30);
for pct in (0..=100).step_by(20) {
println!("{}", bar.render(pct));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zero_percent_is_all_empty() {
let bar = ProgressBar::new("test", 10);
assert_eq!(bar.render(0), format!("{:<20} [{}] {:>3}%", "test", "-".repeat(10), 0));
}
#[test]
fn hundred_percent_is_all_filled() {
let bar = ProgressBar::new("test", 10);
assert_eq!(bar.render(100), format!("{:<20} [{}] {:>3}%", "test", "#".repeat(10), 100));
}
#[test]
fn clamps_above_100() {
let bar = ProgressBar::new("test", 10);
assert_eq!(bar.render(255), bar.render(100));
}
}

86
extras/rust/src/tags.rs Normal file
View file

@ -0,0 +1,86 @@
// a tiny stand-in for the tag struct app/backend/metadata.py works with —
// decorative, the real read/write logic lives in python (mutagen).
#[derive(Debug, Clone)]
pub struct Tag {
pub title: String,
pub artist: String,
pub album: Option<String>,
pub year: Option<u32>,
pub track_number: Option<u32>,
}
impl Tag {
pub fn describe(&self) -> String {
let mut parts = vec![format!("{} — {}", self.artist, self.title)];
if let Some(album) = &self.album {
parts.push(format!("[{}]", album));
}
if let Some(year) = self.year {
parts.push(format!("({})", year));
}
if let Some(track) = self.track_number {
parts.push(format!("track #{}", track));
}
parts.join(" ")
}
pub fn is_complete(&self) -> bool {
!self.title.is_empty() && !self.artist.is_empty()
}
}
pub struct TagBuilder {
tag: Tag,
}
impl TagBuilder {
pub fn new(title: impl Into<String>, artist: impl Into<String>) -> Self {
TagBuilder {
tag: Tag {
title: title.into(),
artist: artist.into(),
album: None,
year: None,
track_number: None,
},
}
}
pub fn album(mut self, album: impl Into<String>) -> Self {
self.tag.album = Some(album.into());
self
}
pub fn year(mut self, year: u32) -> Self {
self.tag.year = Some(year);
self
}
pub fn track_number(mut self, n: u32) -> Self {
self.tag.track_number = Some(n);
self
}
pub fn build(self) -> Tag {
self.tag
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builder_produces_expected_tag() {
let tag = TagBuilder::new("Aria Math", "Logan Feece")
.album("C418 covers")
.year(2024)
.build();
assert!(tag.is_complete());
assert_eq!(tag.describe(), "Logan Feece — Aria Math [C418 covers] (2024)");
}
}