Browse and listen to thousands of radio stations across the globe right from your terminal ๐ ๐ป ๐ตโจ
radio
rust
tokio
web-radio
command-line-tool
tui
1use tunein_cli::{
2 api::{
3 objects::v1alpha1::{Category, Station, StationLinkDetails},
4 tunein::v1alpha1::{
5 browse_service_server::BrowseService, BrowseCategoryRequest, BrowseCategoryResponse,
6 GetCategoriesRequest, GetCategoriesResponse, GetStationDetailsRequest,
7 GetStationDetailsResponse, SearchRequest, SearchResponse,
8 },
9 },
10 provider::radiobrowser::Radiobrowser,
11};
12
13use tunein_cli::provider::{tunein::Tunein, Provider};
14
15#[derive(Default)]
16pub struct Browse;
17
18#[tonic::async_trait]
19impl BrowseService for Browse {
20 async fn get_categories(
21 &self,
22 request: tonic::Request<GetCategoriesRequest>,
23 ) -> Result<tonic::Response<GetCategoriesResponse>, tonic::Status> {
24 let req = request.into_inner();
25 let provider = req.provider.as_deref();
26
27 let client: Box<dyn Provider + Send + Sync> = match provider {
28 Some("tunein") => Box::new(Tunein::new()),
29 Some("radiobrowser") => Box::new(Radiobrowser::new().await),
30 None => Box::new(Tunein::new()),
31 _ => {
32 return Err(tonic::Status::internal("Unsupported provider"));
33 }
34 };
35
36 let offset = req.offset.unwrap_or(0);
37 let limit = req.limit.unwrap_or(100);
38 let result = client
39 .categories(offset, limit)
40 .await
41 .map_err(|e| tonic::Status::internal(e.to_string()))?;
42
43 Ok(tonic::Response::new(GetCategoriesResponse {
44 categories: result.into_iter().map(Category::from).collect(),
45 }))
46 }
47
48 async fn browse_category(
49 &self,
50 request: tonic::Request<BrowseCategoryRequest>,
51 ) -> Result<tonic::Response<BrowseCategoryResponse>, tonic::Status> {
52 let req = request.into_inner();
53 let category_id = req.category_id;
54
55 let offset = req.offset.unwrap_or(0);
56 let limit = req.limit.unwrap_or(100);
57
58 let provider = req.provider.as_deref();
59
60 let client: Box<dyn Provider + Send + Sync> = match provider {
61 Some("tunein") => Box::new(Tunein::new()),
62 Some("radiobrowser") => Box::new(Radiobrowser::new().await),
63 None => Box::new(Tunein::new()),
64 _ => {
65 return Err(tonic::Status::internal("Unsupported provider"));
66 }
67 };
68
69 let results = client
70 .browse(category_id, offset, limit)
71 .await
72 .map_err(|e| tonic::Status::internal(e.to_string()))?;
73
74 let categories = results.into_iter().map(Category::from).collect();
75 Ok(tonic::Response::new(BrowseCategoryResponse { categories }))
76 }
77
78 async fn get_station_details(
79 &self,
80 request: tonic::Request<GetStationDetailsRequest>,
81 ) -> Result<tonic::Response<GetStationDetailsResponse>, tonic::Status> {
82 let req = request.into_inner();
83 let station_id = req.id;
84
85 let provider = req.provider.as_deref();
86
87 let client: Box<dyn Provider + Send + Sync> = match provider {
88 Some("tunein") => Box::new(Tunein::new()),
89 Some("radiobrowser") => Box::new(Radiobrowser::new().await),
90 None => Box::new(Tunein::new()),
91 _ => {
92 return Err(tonic::Status::internal("Unsupported provider"));
93 }
94 };
95
96 let result = client
97 .get_station(station_id)
98 .await
99 .map_err(|e| tonic::Status::internal(e.to_string()))?;
100
101 let station = match result {
102 Some(station) => station,
103 None => return Err(tonic::Status::internal("No station found")),
104 };
105 Ok(tonic::Response::new(GetStationDetailsResponse {
106 station_link_details: Some(StationLinkDetails::from(station)),
107 }))
108 }
109
110 async fn search(
111 &self,
112 request: tonic::Request<SearchRequest>,
113 ) -> Result<tonic::Response<SearchResponse>, tonic::Status> {
114 let req = request.into_inner();
115 let provider = req.provider.as_deref();
116
117 let client: Box<dyn Provider + Send + Sync> = match provider {
118 Some("tunein") => Box::new(Tunein::new()),
119 Some("radiobrowser") => Box::new(Radiobrowser::new().await),
120 None => Box::new(Tunein::new()),
121 _ => {
122 return Err(tonic::Status::internal("Unsupported provider"));
123 }
124 };
125
126 let results = client
127 .search(req.query)
128 .await
129 .map_err(|e| tonic::Status::internal(e.to_string()))?;
130 let station = results.into_iter().map(Station::from).collect();
131 Ok(tonic::Response::new(SearchResponse { station }))
132 }
133}