Skip to content

Commit c5b1de8

Browse files
committed
Fixed lots of bug and improved first contact
1 parent cc28fac commit c5b1de8

File tree

12 files changed

+108
-93
lines changed

12 files changed

+108
-93
lines changed

README.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,23 @@ It's aims to be as fast and simple as possible.
55

66
## Features
77

8-
- Play your Youtube Music SuperMix in the terminal
8+
- Play your Youtube Music Playlist and Supermix.
99
- Memory efficient (Around 20MB of RAM while fully loaded)
1010
- Cache all downloads and store them
1111
- Work even without connection (If musics were already downloaded)
1212
- Automic background download manager
1313

14-
## Installation
14+
## Setup
1515

16-
- Download the lastest version from `releases`
16+
- Download the latest version from `releases`
1717
- Create a `headers.txt` file and copy your headers from the nav when browsing https://music.youtube.com/
18-
- Your headers should be in the following format:
19-
```
20-
HeaderName: HeaderValue
21-
```
18+
- Open the YouTube Music website in your browser");
19+
- Open the developer tools (F12)
20+
- Go to the Network tab
21+
- Go to https://music.youtube.com
22+
- Copy the `cookie` header from the associated request
23+
- Paste it in the `headers.txt` file as `Cookie: <cookie>`
24+
- Restart YterMusic
2225
- Run `ytermusic.exe`
2326

2427
## Screenshots
@@ -29,7 +32,7 @@ It's aims to be as fast and simple as possible.
2932
## Building from source
3033

3134
- Clone the repository
32-
- Install rust `https://rustup.rs`
35+
- Install rust `https://rustup.rs` nightly
3336
- Run `cargo build --release`
3437
- The executable is in `target/release/ytermusic.exe` or `target/release/ytermusic`
3538

@@ -55,6 +58,8 @@ It's aims to be as fast and simple as possible.
5558
- [ ] Cache limit to not exceed some given disk space
5659
- [x] A download limit to stop downloading after the queue is full
5760
- [x] Mouse support
61+
- [x] Search
62+
- [ ] Custom theming
5863

5964
## Changelog
6065

out.html

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

out.json

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/errors.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ use flume::Sender;
22

33
use crate::term::{ManagerMessage, Screens};
44

5+
/**
6+
* Utils to handle errors
7+
*/
58
pub fn handle_error_option<T, E>(
69
updater: &Sender<ManagerMessage>,
710
error_type: &'static str,
@@ -24,6 +27,9 @@ where
2427
}
2528
}
2629

30+
/**
31+
* Utils to handle errors
32+
*/
2733
pub fn handle_error<T>(updater: &Sender<ManagerMessage>, error_type: &'static str, a: Result<(), T>)
2834
where
2935
T: std::fmt::Display,

src/main.rs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,13 @@ mod term;
2424

2525
use mimalloc::MiMalloc;
2626

27+
// Changes the allocator to improve performance especially on Windows
2728
#[global_allocator]
2829
static GLOBAL: MiMalloc = MiMalloc;
2930

31+
/**
32+
* Actions that can be sent to the player from other services
33+
*/
3034
#[derive(Debug, Clone)]
3135
pub enum SoundAction {
3236
Cleanup,
@@ -44,8 +48,12 @@ pub enum SoundAction {
4448
PlayVideoUnary(Video),
4549
}
4650

51+
// A global variable to store the current musical Database
4752
pub static DATABASE: Lazy<RwLock<Vec<Video>>> = Lazy::new(|| RwLock::new(Vec::new()));
4853

54+
/**
55+
* Writes the database to the disk
56+
*/
4957
fn write() {
5058
let db = DATABASE.read().unwrap();
5159
let mut file = OpenOptions::new()
@@ -58,6 +66,9 @@ fn write() {
5866
write_video(&mut file, video)
5967
}
6068
}
69+
/**
70+
* append a video to the database
71+
*/
6172
pub fn append(video: Video) {
6273
let mut file = OpenOptions::new()
6374
.write(true)
@@ -70,6 +81,9 @@ pub fn append(video: Video) {
7081
DATABASE.write().unwrap().push(video);
7182
}
7283

84+
/**
85+
* Writes a video to a file
86+
*/
7387
fn write_video(buffer: &mut impl Write, video: &Video) {
7488
write_str(buffer, &video.title);
7589
write_str(buffer, &video.author);
@@ -78,6 +92,9 @@ fn write_video(buffer: &mut impl Write, video: &Video) {
7892
write_str(buffer, &video.duration);
7993
}
8094

95+
/**
96+
* Reads the database
97+
*/
8198
fn read() -> Option<Vec<Video>> {
8299
let mut buffer = Cursor::new(std::fs::read("data/db.bin").ok()?);
83100
let mut videos = HashSet::new();
@@ -87,6 +104,9 @@ fn read() -> Option<Vec<Video>> {
87104
Some(videos.into_iter().collect::<Vec<_>>())
88105
}
89106

107+
/**
108+
* Reads a video from the cursor
109+
*/
90110
fn read_video(buffer: &mut Cursor<Vec<u8>>) -> Option<Video> {
91111
Some(Video {
92112
title: read_str(buffer)?,
@@ -97,21 +117,33 @@ fn read_video(buffer: &mut Cursor<Vec<u8>>) -> Option<Video> {
97117
})
98118
}
99119

120+
/**
121+
* Writes a string from the cursor
122+
*/
100123
fn write_str(cursor: &mut impl Write, value: &str) {
101124
write_u32(cursor, value.len() as u32);
102125
cursor.write_all(value.as_bytes()).unwrap();
103126
}
104127

128+
/**
129+
* Reads a string from the cursor
130+
*/
105131
fn read_str(cursor: &mut Cursor<Vec<u8>>) -> Option<String> {
106132
let mut buf = vec![0u8; read_u32(cursor)? as usize];
107133
cursor.read_exact(&mut buf).ok()?;
108134
String::from_utf8(buf).ok()
109135
}
110136

137+
/**
138+
* Writes a u32 from the cursor
139+
*/
111140
fn write_u32(cursor: &mut impl Write, value: u32) {
112141
cursor.write_varint(value).unwrap();
113142
}
114143

144+
/**
145+
* Reads a u32 from the cursor
146+
*/
115147
fn read_u32(cursor: &mut Cursor<Vec<u8>>) -> Option<u32> {
116148
ReadVarint::<u32>::read_varint(cursor).ok()
117149
}
@@ -121,18 +153,44 @@ async fn main() -> Result<(), Error> {
121153
std::fs::create_dir_all("data/downloads").unwrap();
122154
if !PathBuf::from_str("headers.txt").unwrap().exists() {
123155
println!("The `headers.txt` file is not present in the root directory.");
124-
println!("This file should contain your headers separated by `: `.");
156+
println!("To configure the YTerMusic:");
157+
println!("1. Open the YouTube Music website in your browser");
158+
println!("2. Open the developer tools (F12)");
159+
println!("3. Go to the Network tab");
160+
println!("4. Go to https://music.youtube.com");
161+
println!("5. Copy the `cookie` header from the associated request");
162+
println!("6. Paste it in the `headers.txt` file as `Cookie: <cookie>`");
163+
println!("7. Restart YterMusic");
125164
return Ok(());
126165
}
166+
if !std::fs::read_to_string("headers.txt")
167+
.unwrap()
168+
.contains("Cookie: ")
169+
{
170+
println!("The `headers.txt` file is not configured correctly.");
171+
println!("To configure the YTerMusic:");
172+
println!("1. Open the YouTube Music website in your browser");
173+
println!("2. Open the developer tools (F12)");
174+
println!("3. Go to the Network tab");
175+
println!("4. Go to https://music.youtube.com");
176+
println!("5. Copy the `cookie` header from the associated request");
177+
println!("6. Paste it in the `headers.txt` file as `Cookie: <cookie>`");
178+
println!("7. Restart YterMusic");
179+
return Ok(());
180+
}
181+
// Spawn the clean task
127182
let (updater_s, updater_r) = flume::unbounded::<ManagerMessage>();
128183
tokio::task::spawn(async {
129184
clean();
130185
});
131186
let updater_s = Arc::new(updater_s);
187+
// Spawn the player task
132188
let (sa, player) = player_system(updater_s.clone());
189+
// Spawn the downloader task
133190
downloader(sa.clone());
134191
{
135192
let updater_s = updater_s.clone();
193+
// Spawn playlist updater task
136194
tokio::task::spawn(async move {
137195
let playlist = std::fs::read_to_string("data/last-playlist.json").ok()?;
138196
let mut playlist: (String, Vec<Video>) = serde_json::from_str(&playlist).ok()?;
@@ -147,6 +205,7 @@ async fn main() -> Result<(), Error> {
147205
}
148206
{
149207
let updater_s = updater_s.clone();
208+
// Spawn the API task
150209
tokio::task::spawn(async move {
151210
match YTApi::from_header_file(PathBuf::from_str("headers.txt").unwrap().as_path()).await
152211
{
@@ -187,6 +246,7 @@ async fn main() -> Result<(), Error> {
187246
}
188247
{
189248
let updater_s = updater_s.clone();
249+
// Spawn the database getter task
190250
tokio::task::spawn(async move {
191251
if let Some(e) = read() {
192252
*DATABASE.write().unwrap() = e.clone();
@@ -228,6 +288,9 @@ async fn main() -> Result<(), Error> {
228288
Ok(())
229289
}
230290

291+
/**
292+
* This function is called on strat to clean the database and the files that are incompletly downloaded due to a crash.
293+
*/
231294
fn clean() {
232295
for i in std::fs::read_dir("data/downloads").unwrap() {
233296
let path = i.unwrap().path();

src/systems/download.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub fn add(video: Video, s: &Sender<SoundAction>) {
4343
let download_path_json =
4444
PathBuf::from_str(&format!("data/downloads/{}.json", &video.video_id)).unwrap();
4545
if download_path_json.exists() {
46-
s.send(SoundAction::PlayVideo(video.clone())).unwrap();
46+
s.send(SoundAction::PlayVideo(video)).unwrap();
4747
} else {
4848
DOWNLOAD_QUEUE.lock().unwrap().push_back(video);
4949
}
@@ -55,7 +55,7 @@ async fn handle_download(id: &str) -> Result<PathBuf, Error> {
5555
.streams()
5656
.iter()
5757
.filter(|stream| {
58-
stream.mime.to_string() == "audio/mp4"
58+
stream.mime == "audio/mp4"
5959
&& stream.includes_audio_track
6060
&& !stream.includes_video_track
6161
})

src/systems/player.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::{
1616
SoundAction, DATABASE,
1717
};
1818

19-
use super::download::{DOWNLOAD_MORE, IN_DOWNLOAD};
19+
use super::download::IN_DOWNLOAD;
2020

2121
#[cfg(not(target_os = "windows"))]
2222
fn get_handle(updater: &Sender<ManagerMessage>) -> Option<MediaControls> {
@@ -54,7 +54,7 @@ fn get_handle(updater: &Sender<ManagerMessage>) -> Option<MediaControls> {
5454
updater
5555
.send(ManagerMessage::PassTo(
5656
Screens::DeviceLost,
57-
Box::new(ManagerMessage::Error(format!("No window handle found"))),
57+
Box::new(ManagerMessage::Error("No window handle found".to_string())),
5858
))
5959
.unwrap();
6060
return None;
@@ -162,17 +162,15 @@ impl PlayerState {
162162
.unwrap();
163163
}
164164
}
165-
} else {
166-
if let Some(e) = self.current.take() {
167-
self.previous.push(e);
168-
}
165+
} else if let Some(e) = self.current.take() {
166+
self.previous.push(e);
169167
}
170168
}
171169
}
172170

173171
fn handle_stream_errors(&self) {
174172
while let Ok(e) = self.stream_error_receiver.try_recv() {
175-
let _ = handle_error(&self.updater, "audio device stream error", Err(e));
173+
handle_error(&self.updater, "audio device stream error", Err(e));
176174
}
177175
}
178176
fn update_controls(&mut self) {
@@ -198,7 +196,6 @@ impl PlayerState {
198196
})?;
199197
}
200198
}
201-
()
202199
};
203200
k.map_err(|x| format!("{:?}", x))
204201
});
@@ -332,9 +329,10 @@ pub fn get_action(
332329
index -= 1;
333330
}
334331
if queue.len() < index {
335-
return None;
332+
None
333+
} else {
334+
Some(MusicStatusAction::Skip(index + 1))
336335
}
337-
return Some(MusicStatusAction::Skip(index + 1));
338336
}
339337

340338
pub fn generate_music<'a>(

src/term/error_screen.rs

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

0 commit comments

Comments
 (0)