Skip to content

Commit 1517d31

Browse files
committed
refactor: Move DB code to a DB crate.
1 parent 465405b commit 1517d31

File tree

15 files changed

+244
-207
lines changed

15 files changed

+244
-207
lines changed

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
[workspace]
22
resolver = "3"
3-
members = ["crates/player", "crates/ytermusic", "crates/ytpapi2"]
3+
members = ["crates/database","crates/player", "crates/ytermusic", "crates/ytpapi2"]
44

55
[workspace.dependencies]
66
player = { path = "crates/player" }
7-
ytpapi2 = { path = "crates/ytpapi2" }
7+
ytpapi2 = { path = "crates/ytpapi2" }
8+
database = { path = "crates/database" }

crates/database/Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "database"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
varuint = "0.7.1"
8+
ytpapi2.workspace = true
9+
log = "*"
10+
serde_json = "1.0.114"

crates/database/src/lib.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use std::{fs::OpenOptions, path::PathBuf, sync::RwLock};
2+
3+
mod reader;
4+
mod writer;
5+
6+
pub use writer::write_video;
7+
use ytpapi2::YoutubeMusicVideoRef;
8+
9+
pub struct YTLocalDatabase {
10+
cache_dir: PathBuf,
11+
references: RwLock<Vec<YoutubeMusicVideoRef>>,
12+
}
13+
14+
impl YTLocalDatabase {
15+
pub fn new(cache_dir: PathBuf) -> Self {
16+
Self {
17+
cache_dir,
18+
references: RwLock::new(Vec::new()),
19+
}
20+
}
21+
22+
pub fn clone_from(&self, videos: &Vec<YoutubeMusicVideoRef>) {
23+
self.references.write().unwrap().clone_from(videos);
24+
}
25+
26+
pub fn remove_video(&self, video: &YoutubeMusicVideoRef) {
27+
let mut database = self.references.write().unwrap();
28+
database.retain(|v| v.video_id != video.video_id);
29+
drop(database);
30+
self.write();
31+
}
32+
33+
pub fn append(&self, video: YoutubeMusicVideoRef) {
34+
let mut file = OpenOptions::new()
35+
.append(true)
36+
.create(true)
37+
.open(self.cache_dir.join("db.bin"))
38+
.unwrap();
39+
write_video(&mut file, &video);
40+
self.references.write().unwrap().push(video);
41+
}
42+
}
Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@ use std::io::{Cursor, Read};
33
use varuint::ReadVarint;
44
use ytpapi2::YoutubeMusicVideoRef;
55

6-
use crate::consts::CACHE_DIR;
6+
use crate::YTLocalDatabase;
77

8-
/// Reads the database
9-
pub fn read() -> Option<Vec<YoutubeMusicVideoRef>> {
10-
let mut buffer = Cursor::new(std::fs::read(CACHE_DIR.join("db.bin")).ok()?);
11-
let mut videos = Vec::new();
12-
while buffer.get_mut().len() > buffer.position() as usize {
13-
videos.push(read_video(&mut buffer)?);
8+
impl YTLocalDatabase {
9+
pub fn read(&self) -> Option<Vec<YoutubeMusicVideoRef>> {
10+
let mut buffer = Cursor::new(std::fs::read(self.cache_dir.join("db.bin")).ok()?);
11+
let mut videos = Vec::new();
12+
while buffer.get_mut().len() > buffer.position() as usize {
13+
videos.push(read_video(&mut buffer)?);
14+
}
15+
Some(videos)
1416
}
15-
Some(videos)
1617
}
1718

1819
/// Reads a video from the cursor

crates/database/src/writer.rs

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
use std::{fs::OpenOptions, io::Write};
2+
3+
use varuint::WriteVarint;
4+
use ytpapi2::YoutubeMusicVideoRef;
5+
6+
use crate::YTLocalDatabase;
7+
8+
impl YTLocalDatabase {
9+
pub fn write(&self) {
10+
let db = self.references.read().unwrap();
11+
let mut file = OpenOptions::new()
12+
.write(true)
13+
.append(false)
14+
.create(true)
15+
.truncate(true)
16+
.open(self.cache_dir.join("db.bin"))
17+
.unwrap();
18+
for video in db.iter() {
19+
write_video(&mut file, video)
20+
}
21+
}
22+
}
23+
impl YTLocalDatabase {
24+
pub fn fix_db(&self) {
25+
let mut db = self.references.write().unwrap();
26+
db.clear();
27+
let cache_folder = self.cache_dir.join("downloads");
28+
if !cache_folder.is_dir() {
29+
println!(
30+
"[WARN] The download folder in the cache wasn't found ({:?})",
31+
cache_folder
32+
);
33+
return;
34+
}
35+
for entry in std::fs::read_dir(&cache_folder).unwrap() {
36+
let entry = entry.unwrap();
37+
let path = entry.path();
38+
// Check if the file is a json file (+ sloppy check if there is any files or directory)
39+
if path.extension().unwrap_or_default() != "json" {
40+
continue;
41+
}
42+
// Read the file if not readable do not add it to the database
43+
let content = match std::fs::read_to_string(&path) {
44+
Ok(content) => content,
45+
Err(e) => {
46+
match std::fs::remove_file(&path) {
47+
Ok(_) => println!(
48+
"[INFO] Removing file {:?} because the file is not readable: {e:?}",
49+
path.file_name()
50+
),
51+
Err(ef) => println!(
52+
"[ERROR] file {:?} is not readable: {e:?}, but could not be deleted: {ef:?}",
53+
path.file_name()
54+
),
55+
}
56+
continue;
57+
}
58+
};
59+
// Check if the file is a valid json file
60+
let video = match serde_json::from_str::<YoutubeMusicVideoRef>(&content) {
61+
Ok(parsed) => parsed,
62+
Err(e) => {
63+
match std::fs::remove_file(&path) {
64+
Ok(_) => println!(
65+
"[INFO] Removing file {:?} because the file is not a valid json file: {e:?}",
66+
path.file_name()
67+
),
68+
Err(ef) => println!(
69+
"[ERROR] file {:?} is not a valid json file: {e:?}, but could not be deleted: {ef:?}",
70+
path.file_name()
71+
),
72+
}
73+
continue;
74+
}
75+
};
76+
// Check if the video file exists
77+
let video_file = cache_folder.join(format!("{}.mp4", video.video_id));
78+
if !video_file.exists() {
79+
match std::fs::remove_file(&path) {
80+
Ok(_) => println!(
81+
"[INFO] Removing file {:?} because the video file does not exist",
82+
path.file_name()
83+
),
84+
Err(ef) => println!(
85+
"[ERROR] video assocated to file {:?} does not exist, but the file could not be deleted: {ef:?}",
86+
path.file_name()
87+
),
88+
}
89+
continue;
90+
}
91+
// Read the video file
92+
let video_file = match std::fs::read(&video_file) {
93+
Ok(video_file) => video_file,
94+
Err(e) => {
95+
match std::fs::remove_file(&path) {
96+
Ok(_) => println!(
97+
"[INFO] Removing file {:?} because the video file is not readable: {e:?}",
98+
path.file_name()
99+
),
100+
Err(ef) => println!(
101+
"[ERROR] video associated to file {:?} is not readable: {e:?}, but the file could not be deleted: {ef:?}",
102+
path.file_name()
103+
),
104+
}
105+
continue;
106+
}
107+
};
108+
// Check if the video file contains the header
109+
if !video_file.starts_with(&[
110+
0, 0, 0, 24, 102, 116, 121, 112, 100, 97, 115, 104, 0, 0, 0, 0,
111+
]) {
112+
match std::fs::remove_file(&path) {
113+
Ok(_) => println!(
114+
"[INFO] Removing file {:?} because the video file does not contain the header",
115+
path.file_name()
116+
),
117+
Err(ef) => println!(
118+
"[ERROR] video associated to file {:?} does not contain the header, but the file could not be deleted: {ef:?}",
119+
path.file_name()
120+
),
121+
}
122+
continue;
123+
}
124+
db.push(video);
125+
}
126+
}
127+
}
128+
129+
/// Writes a video to a file
130+
pub fn write_video(buffer: &mut impl Write, video: &YoutubeMusicVideoRef) {
131+
write_str(buffer, &video.title);
132+
write_str(buffer, &video.author);
133+
write_str(buffer, &video.album);
134+
write_str(buffer, &video.video_id);
135+
write_str(buffer, &video.duration);
136+
}
137+
138+
/// Writes a string from the cursor
139+
fn write_str(cursor: &mut impl Write, value: &str) {
140+
write_u32(cursor, value.len() as u32);
141+
cursor.write_all(value.as_bytes()).unwrap();
142+
}
143+
144+
/// Writes a u32 from the cursor
145+
fn write_u32(cursor: &mut impl Write, value: u32) {
146+
cursor.write_varint(value).unwrap();
147+
}

crates/ytermusic/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ toml = "0.8.11"
4444
# --- Logging ---
4545
log = "0.4.21"
4646

47+
# --- Database ---
48+
database.workspace = true
49+
4750
# -- Cookies auto retreival --
4851
rookie = "0.5.2"
4952

crates/ytermusic/src/database.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
use database::YTLocalDatabase;
2+
use once_cell::sync::Lazy;
3+
4+
use crate::consts::CACHE_DIR;
5+
6+
pub static DATABASE: Lazy<YTLocalDatabase> = Lazy::new(|| YTLocalDatabase::new(CACHE_DIR.clone()));

crates/ytermusic/src/database/mod.rs

Lines changed: 0 additions & 37 deletions
This file was deleted.

0 commit comments

Comments
 (0)