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
#[macro_export]
macro_rules! value_to_string {
($v: expr) => {
match $v {
serde_json::Value::String(ref s) => Some(s.to_owned()),
_ => None,
}
};
($v: expr, $($or: expr),+) => {
match $v {
serde_json::Value::String(ref s) => Some(s.to_owned()),
_ => $crate::value_to_string!($($or),+),
}
};
}
#[macro_export]
macro_rules! parse_json {
($s: expr) => {
match serde_json::from_str($s) {
Ok(v) => v,
Err(_) => return Err(anyhow::anyhow!(format!("Invalid json data: {}", $s))),
}
};
($s: expr, $ty: ty) => {
match serde_json::from_str($s) {
Ok::<$ty, _>(v) => v,
Err(_) => return Err(anyhow::anyhow!(format!("Invalid json data: {}", $s))),
}
};
}
#[macro_export]
macro_rules! get {
($v: expr) => {
$v
};
($v: expr, $($vn: expr),+) => {
match $v {
serde_json::Value::Null => $crate::get!($($vn),+),
_ => $v,
}
}
}
pub mod proxy;
pub mod command;
pub mod extractors;
pub mod parsers;
use anyhow::Result;
use std::process::Command;
use std::io::Result as IoResult;
use proxy::ProxyAddr;
use parsers::Url;
type ResultInfo = Result<MediaInfo>;
#[derive(Debug)]
pub struct MediaInfo {
pub url: Url,
pub title: Option<String>,
pub referrer: Option<String>,
pub user_agent: Option<String>,
}
pub struct Setting<'a> {
pub proxy_addr: Option<ProxyAddr<'a>>,
pub cookie: Option<&'a str>,
}
impl MediaInfo {
pub fn new(url: Url, title: Option<String>, referrer: Option<String>) -> Self {
Self::with_ua(url, title, referrer, None)
}
pub fn with_ua(url: Url, title: Option<String>, referrer: Option<String>, user_agent: Option<String>) -> Self {
Self { url, title, referrer, user_agent }
}
pub fn default_ua(url: Url, title: Option<String>, referrer: Option<String>) -> Self {
Self::with_ua(url, title, referrer, Some(String::from("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.4 Safari/537.36")))
}
pub fn play(&self) -> IoResult<()> {
self.as_command().output()?;
Ok(())
}
pub fn as_command(&self) -> Command {
let Url { videos, audios } = &self.url;
let mut cmd = Command::new("mpv");
match videos.len() {
0 => cmd.args(audios.iter())
.arg("--force-window=immediate"),
1 => cmd.arg(&videos[0])
.args(audios
.iter()
.map(|a| format!("--audio-file={}", a))
),
_ => cmd.args(videos.iter())
.args(audios
.iter()
.map(|a| format!("--audio-file={}", a))
)
.arg("--merge-files"),
};
if let Some(referrer) = &self.referrer {
cmd.arg(format!("--referrer={}", referrer));
}
if let Some(title) = &self.title {
cmd.arg(format!("--title={}", title));
}
if let Some(user_agent) = &self.user_agent {
cmd.arg(format!("--user-agent={}", user_agent));
}
cmd.arg("--no-ytdl");
cmd
}
}
impl<'a> From<ProxyAddr<'a>> for Setting<'a> {
fn from(proxy_addr: ProxyAddr<'a>) -> Setting<'a> {
Setting { proxy_addr: Some(proxy_addr), cookie: None }
}
}
impl<'a> From<Option<ProxyAddr<'a>>> for Setting<'a> {
fn from(proxy_addr: Option<ProxyAddr<'a>>) -> Setting<'a> {
Setting { proxy_addr: proxy_addr, cookie: None }
}
}
impl<'a> AsRef<Option<ProxyAddr<'a>>> for Setting<'a> {
fn as_ref(&self) -> &Option<ProxyAddr<'a>> {
&self.proxy_addr
}
}