NeuroWhAI의 잡블로그

[Rust] Youtube playlist를 mp3 파일들로 다운로드 본문

개발 및 공부/언어

[Rust] Youtube playlist를 mp3 파일들로 다운로드

NeuroWhAI 2018. 1. 2. 18:20


대충 아래처럼 작동 합니다.


Playlist ID: PLETWVpERkQEiFcR61q36BMeOI6prNr0ci
1. 【廃墟系】けもフレEDを最終回っぽくしてみた【アレンジ】
2. Kemono Friends ED - Boku no Friend (Simpsonill Remix)
Choose the video number to download.
(0: All, q: exit): 0
Start download.
Download MbjtzH9JIKo
Title: 【廃墟系】けもフレEDを最終回っぽくしてみた【アレンジ】
Quality: 128030
2121970 / 2121970 ╢▌▌▌▌▌▌▌▌▌▌▌╟ 100.00 % 79234.86/s
Start conversion.
Conversion complete.
Done.
Download mOznRejcPfs
Title: Kemono Friends ED - Boku no Friend (Simpsonill Remix)
Quality: 128081
2529085 / 2529085 ╢▌▌▌▌▌▌▌▌▌▌▌╟ 100.00 % 74822.94/s
Start conversion.
Conversion complete.
Done.
Complete!

라이브러리만 가져다 쓴거라서 딱히 기술적으로 어려운 코드는 없습니다.

m4a 파일을 mp3로 변환하려고 ffmpeg를 썼는데
그래서 실행하려면 작업 경로에 ffmpeg.exe가 있어야 합니다 ㅠㅠ

아래는 소스코드랑 의존성.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
extern crate reqwest;
extern crate serde_json;
extern crate rafy;
 
 
use serde_json::Value;
use std::io::{stdin, stdout, Read, Write};
use rafy::Rafy;
use std::process::Command;
 
 
struct VideoInfo {
    id: String,
    title: String,
}
 
 
fn get_json(uri: &String) -> Option<Value> {
    let mut res = reqwest::get(uri).unwrap();
 
    if res.status().is_success() {
        let mut data = Vec::new();
        res.read_to_end(&mut data).unwrap();
 
        Some(serde_json::from_slice(&data.as_slice()).unwrap())
    }
    else {
        None
    }
}
 
fn get_playlist_items(playlist_id: &str) -> Vec<VideoInfo> {
    let mut result = Vec::new();
 
    let uri = format!("https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&playlistId={}&key={}",
        playlist_id, "YOUR_API_KEY");
 
    if let Some(list_json) = get_json(&uri) {
        let items = list_json["items"].as_array().unwrap();
 
        for info in items {
            let video_id = info["snippet"]["resourceId"]["videoId"].as_str().unwrap();
            let video_title = info["snippet"]["title"].as_str().unwrap();
 
            result.push(VideoInfo {
                id: video_id.to_owned(),
                title: video_title.to_owned(),
            });
        }
    }
    else {
        println!("Fail to get a playlist.");
    }
 
    result
}
 
fn download(id: &String, title: &String) {
    println!("  Download {}", id);
    println!("    Title: {}", title);
 
    let uri = format!("https://www.youtube.com/watch?v={}", id);
    let content = Rafy::new(&uri).unwrap();
 
    let title = content.title;
    let audiostreams = content.audiostreams;
 
    println!("    Quality: {}", audiostreams[0].quality);
 
    audiostreams[0].download(&title).unwrap();
 
    println!("");
 
    convert(&format!("{}.{}", title, audiostreams[0].extension), &title);
 
    println!("    Done.");
}
 
fn convert(file: &String, title: &String) {
    println!("    Start conversion.");
 
    // https://www.ffmpeg.org/
    let mut ffmpeg = std::env::current_dir().unwrap();
    ffmpeg.push("ffmpeg");
 
    let output = Command::new(ffmpeg)
        .arg("-i").arg(file)
        .arg("-acodec").arg("libmp3lame")
        .arg("-aq").arg("2")
        .arg(format!("{}.mp3", title))
        .output()
        .expect("Failed to run ffmpeg.");
 
    if output.status.success() {
        std::fs::remove_file(file).unwrap();
 
        println!("    Conversion complete.");
    }
    else {
        println!("    Failed to convert {}", file);
    }
}
 
fn main() {
    print!("Playlist ID: ");
    stdout().flush().unwrap();
 
    let mut playlist_id = String::new();
    stdin().read_line(&mut playlist_id).unwrap();
 
    let list = get_playlist_items(&playlist_id.trim());
 
    loop {
        for (index, info) in list.iter().enumerate() {
            println!("{}. {}", index + 1&info.title);
        }
 
        println!("Choose the video number to download.");
        print!("(0: All, q: exit): ");
        stdout().flush().unwrap();
 
        let mut buf = String::new();
        stdin().read_line(&mut buf).unwrap();
 
        let choice: i32 = match buf.trim() {
            "q" | "Q" | "quit" | "Quit" | "exit" | "Exit" => break,
            numstr @ _ => numstr.parse().unwrap_or(-1),
        };
 
        if choice < 0 || choice as usize > list.len() {
            println!("");
            continue;
        }
 
        println!("Start download.");
 
        if choice == 0 {
            // Download all.
            for info in &list {
                download(&info.id, &info.title);
            }
 
            println!("Complete!");
 
            break;
        }
        else {
            // Download one.
            let ref one = list[choice as usize - 1];
            download(&one.id, &one.title);
        }
 
        println!("");
    }
}
cs
[dependencies]
reqwest = "0.7.3"
serde_json = "1.0.2"
rafy = "0.2"

원래 친구님이 필요하다고해서 묵혀뒀다가 Rust 공부할 겸 만들기 시작했는데
이미 좋은 툴이 많아서 그냥 그거 쓰라고하고 이렇게 올린겁니다.
하핫...

아 전체공개 되어있지 않은 재생목록은 안됩니다...





Comments