обновление

This commit is contained in:
2026-10-13 17:38:18 +02:00
parent 255a99ed0f
commit 08a8a7cce5
6 changed files with 502 additions and 87 deletions

202
src/library.rs Normal file
View File

@@ -0,0 +1,202 @@
use crate::track::Track;
#[derive(Clone)]
pub enum LeftRow {
Artist { name: String },
Album { name: String },
}
#[derive(Clone)]
pub enum RightRow {
Track { index_in_library: usize },
}
#[derive(Clone, Copy, PartialEq)]
pub enum SortMode {
ByArtist,
ByFilename,
}
pub struct Library {
pub all_tracks: Vec<Track>,
/// Левая панель: артисты и альбомы
pub left_rows: Vec<LeftRow>,
/// Для каждой строки левой панели — список индексов треков в all_tracks
pub left_to_tracks: Vec<Vec<usize>>,
/// Индекс выбранной строки левой панели
pub left_selected: usize,
/// Индекс выбранного трека в правой панели (внутри текущего набора)
pub right_selected: usize,
pub sort_mode: SortMode,
}
impl Library {
pub fn build(mut tracks: Vec<Track>, mode: SortMode) -> Self {
match mode {
SortMode::ByArtist => {
use std::collections::BTreeMap;
let mut map: BTreeMap<String, BTreeMap<String, Vec<Track>>> = BTreeMap::new();
for t in tracks.drain(..) {
map.entry(t.artist.clone())
.or_default()
.entry(t.album.clone())
.or_default()
.push(t);
}
let mut all_tracks = Vec::new();
let mut left_rows = Vec::new();
let mut left_to_tracks: Vec<Vec<usize>> = Vec::new();
for (artist_name, albums_map) in map {
// Строка артиста: содержит все треки всех его альбомов
let mut artist_track_indices = Vec::new();
let mut album_entries = Vec::new();
for (album_name, mut album_tracks) in albums_map {
album_tracks.sort_by(|a, b| {
a.track_number
.unwrap_or(u32::MAX)
.cmp(&b.track_number.unwrap_or(u32::MAX))
.then(a.title.cmp(&b.title))
});
let mut album_indices = Vec::new();
for t in album_tracks {
let idx = all_tracks.len();
all_tracks.push(t);
album_indices.push(idx);
artist_track_indices.push(idx);
}
album_entries.push((album_name, album_indices));
}
// 1) строка артиста
left_rows.push(LeftRow::Artist {
name: artist_name,
});
left_to_tracks.push(artist_track_indices);
// 2) строки альбомов
for (album_name, indices) in album_entries {
left_rows.push(LeftRow::Album { name: album_name });
left_to_tracks.push(indices);
}
}
Library {
all_tracks,
left_rows,
left_to_tracks,
left_selected: 0,
right_selected: 0,
sort_mode: mode,
}
}
SortMode::ByFilename => {
tracks.sort_by(|a, b| a.path.cmp(&b.path));
let all_tracks = tracks;
let left_rows = vec![LeftRow::Album {
name: "Все файлы".into(),
}];
let left_to_tracks = vec![(0..all_tracks.len()).collect()];
Library {
all_tracks,
left_rows,
left_to_tracks,
left_selected: 0,
right_selected: 0,
sort_mode: mode,
}
}
}
}
pub fn track(&self, index: usize) -> Option<&Track> {
self.all_tracks.get(index)
}
/// Индексы треков, относящихся к текущей выделенной строке левой панели
pub fn current_right_indices(&self) -> &[usize] {
self.left_to_tracks
.get(self.left_selected)
.map(|v| v.as_slice())
.unwrap_or(&[])
}
/// Индекс трека в all_tracks, выделенного в правой панели
pub fn right_track_index(&self) -> Option<usize> {
self.current_right_indices()
.get(self.right_selected)
.copied()
}
/// Найти строку левой панели, к которой относится трек (для синхронизации)
pub fn left_row_of_track(&self, index_in_library: usize) -> Option<usize> {
// Приоритет — альбом (более точное совпадение)
let mut album_match = None;
for (i, row) in self.left_rows.iter().enumerate() {
let contains = self
.left_to_tracks
.get(i)
.map(|v| v.contains(&index_in_library))
.unwrap_or(false);
if contains {
match row {
LeftRow::Album { .. } => return Some(i),
LeftRow::Artist { .. } => album_match = Some(i),
}
}
}
album_match
}
/// Передвинуть курсор левой панели вверх/вниз, синхронизировать правую
pub fn left_move(&mut self, delta: isize) {
if self.left_rows.is_empty() {
return;
}
let len = self.left_rows.len() as isize;
let mut new = self.left_selected as isize + delta;
if new < 0 {
new = len - 1;
} else if new >= len {
new = 0;
}
self.left_selected = new as usize;
self.right_selected = 0;
}
/// Передвинуть курсор правой панели вверх/вниз
pub fn right_move(&mut self, delta: isize) {
let len = self.current_right_indices().len() as isize;
if len == 0 {
return;
}
let mut new = self.right_selected as isize + delta;
if new < 0 {
new = len - 1;
} else if new >= len {
new = 0;
}
self.right_selected = new as usize;
}
/// Синхронизировать правую панель с играющим треком
pub fn sync_with_playing(&mut self, index_in_library: usize) {
if let Some(row) = self.left_row_of_track(index_in_library) {
self.left_selected = row;
if let Some(pos) = self
.left_to_tracks
.get(row)
.and_then(|v| v.iter().position(|&i| i == index_in_library))
{
self.right_selected = pos;
}
}
}
}