mangofetch_core/platforms/
mod.rs1pub mod traits;
2
3#[cfg(feature = "platforms")]
4pub mod bilibili;
5#[cfg(feature = "platforms")]
6pub mod bluesky;
7#[cfg(feature = "platforms")]
8pub mod generic_ytdlp;
9#[cfg(feature = "platforms")]
10pub mod instagram;
11#[cfg(feature = "platforms")]
12pub mod magnet;
13#[cfg(feature = "platforms")]
14pub mod p2p;
15#[cfg(feature = "platforms")]
16pub mod pinterest;
17#[cfg(feature = "platforms")]
18pub mod reddit;
19#[cfg(feature = "platforms")]
20pub mod tiktok;
21#[cfg(feature = "platforms")]
22pub mod twitch;
23#[cfg(feature = "platforms")]
24pub mod twitter;
25#[cfg(feature = "platforms")]
26pub mod vimeo;
27#[cfg(feature = "platforms")]
28pub mod youtube;
29
30use serde::{Deserialize, Serialize};
31use std::fmt;
32use std::str::FromStr;
33
34#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
35#[serde(rename_all = "lowercase")]
36pub enum Platform {
37 Hotmart,
38 YouTube,
39 Instagram,
40 TikTok,
41 Twitter,
42 Reddit,
43 Twitch,
44 Pinterest,
45 Bluesky,
46 Telegram,
47 Vimeo,
48 Udemy,
49 Bilibili,
50 Other(String),
51}
52
53impl fmt::Display for Platform {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 let name = match self {
56 Platform::Hotmart => "hotmart",
57 Platform::YouTube => "youtube",
58 Platform::Instagram => "instagram",
59 Platform::TikTok => "tiktok",
60 Platform::Twitter => "twitter",
61 Platform::Reddit => "reddit",
62 Platform::Twitch => "twitch",
63 Platform::Pinterest => "pinterest",
64 Platform::Bluesky => "bluesky",
65 Platform::Telegram => "telegram",
66 Platform::Vimeo => "vimeo",
67 Platform::Udemy => "udemy",
68 Platform::Bilibili => "bilibili",
69 Platform::Other(ref name) => name.as_str(),
70 };
71 write!(f, "{}", name)
72 }
73}
74
75impl FromStr for Platform {
76 type Err = String;
77
78 fn from_str(s: &str) -> Result<Self, Self::Err> {
79 match s.to_lowercase().as_str() {
80 "hotmart" => Ok(Platform::Hotmart),
81 "youtube" | "yt" => Ok(Platform::YouTube),
82 "instagram" | "ig" => Ok(Platform::Instagram),
83 "tiktok" | "tt" => Ok(Platform::TikTok),
84 "twitter" | "x" => Ok(Platform::Twitter),
85 "reddit" => Ok(Platform::Reddit),
86 "twitch" => Ok(Platform::Twitch),
87 "pinterest" => Ok(Platform::Pinterest),
88 "bluesky" | "bsky" => Ok(Platform::Bluesky),
89 "telegram" | "tg" => Ok(Platform::Telegram),
90 "vimeo" => Ok(Platform::Vimeo),
91 "udemy" => Ok(Platform::Udemy),
92 "bilibili" | "b站" => Ok(Platform::Bilibili),
93 _ => Err(format!("Unknown platform: {}", s)),
94 }
95 }
96}
97
98impl Platform {
99 pub fn from_url(url_str: &str) -> Option<Self> {
100 if url_str.starts_with("p2p:") {
102 return Some(Platform::Other("p2p".to_string()));
103 }
104
105 if url_str.starts_with("magnet:") || url_str.ends_with(".torrent") {
108 return Some(Platform::Other("magnet".to_string()));
109 }
110
111 let parsed = url::Url::parse(url_str).ok()?;
112 let host = parsed.host_str()?.to_lowercase();
113
114 let matches =
115 |domain: &str| -> bool { host == domain || host.ends_with(&format!(".{}", domain)) };
116
117 if matches("hotmart.com") {
118 Some(Platform::Hotmart)
119 } else if matches("youtube.com") || matches("youtube-nocookie.com") || host == "youtu.be" {
120 Some(Platform::YouTube)
121 } else if matches("instagram.com") || matches("ddinstagram.com") {
122 Some(Platform::Instagram)
123 } else if matches("tiktok.com") {
124 Some(Platform::TikTok)
125 } else if matches("twitter.com")
126 || matches("x.com")
127 || matches("vxtwitter.com")
128 || matches("fixvx.com")
129 {
130 Some(Platform::Twitter)
131 } else if matches("reddit.com") || host == "v.redd.it" || host == "redd.it" {
132 Some(Platform::Reddit)
133 } else if matches("twitch.tv") {
134 Some(Platform::Twitch)
135 } else if host == "pin.it" || host.contains("pinterest.") {
136 Some(Platform::Pinterest)
137 } else if host == "bsky.app" || host.ends_with(".bsky.app") {
138 Some(Platform::Bluesky)
139 } else if host == "t.me" || matches("telegram.me") || matches("telegram.org") {
140 Some(Platform::Telegram)
141 } else if matches("vimeo.com") {
142 Some(Platform::Vimeo)
143 } else if matches("udemy.com") {
144 Some(Platform::Udemy)
145 } else if matches("bilibili.com") || matches("bilibili.tv") || host == "b23.tv" {
146 Some(Platform::Bilibili)
147 } else if matches("kiwify.com.br") {
148 Some(Platform::Other("kiwify".to_string()))
149 } else if matches("gumroad.com") {
150 Some(Platform::Other("gumroad".to_string()))
151 } else if matches("teachable.com") {
152 Some(Platform::Other("teachable".to_string()))
153 } else if matches("kajabi.com") {
154 Some(Platform::Other("kajabi".to_string()))
155 } else if matches("skool.com") {
156 Some(Platform::Other("skool".to_string()))
157 } else if matches("thegreatcoursesplus.com") || matches("wondrium.com") {
158 Some(Platform::Other("greatcourses".to_string()))
159 } else if matches("thinkific.com") {
160 Some(Platform::Other("thinkific".to_string()))
161 } else if matches("rocketseat.com.br") {
162 Some(Platform::Other("rocketseat".to_string()))
163 } else if matches("douyin.com") || matches("iesdouyin.com") {
164 Some(Platform::Other("douyin".to_string()))
165 } else if matches("kuaishou.com") {
166 Some(Platform::Other("kuaishou".to_string()))
167 } else if matches("xiaohongshu.com") || matches("xhslink.com") {
168 Some(Platform::Other("xiaohongshu".to_string()))
169 } else if matches("v.qq.com") || (matches("qq.com") && parsed.path().starts_with("/x/")) {
170 Some(Platform::Other("tencentvideo".to_string()))
171 } else if matches("iqiyi.com") {
172 Some(Platform::Other("iqiyi".to_string()))
173 } else if matches("mgtv.com") {
174 Some(Platform::Other("mgtv".to_string()))
175 } else if matches("youku.com") {
176 Some(Platform::Other("youku".to_string()))
177 } else {
178 None
179 }
180 }
181
182 pub fn all() -> &'static [Platform] {
183 &[
184 Platform::Hotmart,
185 Platform::YouTube,
186 Platform::Instagram,
187 Platform::TikTok,
188 Platform::Twitter,
189 Platform::Reddit,
190 Platform::Twitch,
191 Platform::Pinterest,
192 Platform::Bluesky,
193 Platform::Telegram,
194 Platform::Vimeo,
195 Platform::Udemy,
196 Platform::Bilibili,
197 ]
198 }
199}