Browse and listen to thousands of radio stations across the globe right from your terminal ๐ŸŒŽ ๐Ÿ“ป ๐ŸŽตโœจ
radio rust tokio web-radio command-line-tool tui
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: preserve original station ID when creating Station from StationLinkDetails

+289 -128
+235 -125
src/api/tunein.v1alpha1.rs
··· 39 39 #[derive(Clone, PartialEq, ::prost::Message)] 40 40 pub struct GetStationDetailsResponse { 41 41 #[prost(message, optional, tag = "1")] 42 - pub station_link_details: 43 - ::core::option::Option<super::super::objects::v1alpha1::StationLinkDetails>, 42 + pub station_link_details: ::core::option::Option< 43 + super::super::objects::v1alpha1::StationLinkDetails, 44 + >, 44 45 } 45 46 #[derive(Clone, PartialEq, ::prost::Message)] 46 47 pub struct SearchRequest { ··· 61 62 dead_code, 62 63 missing_docs, 63 64 clippy::wildcard_imports, 64 - clippy::let_unit_value 65 + clippy::let_unit_value, 65 66 )] 66 - use tonic::codegen::http::Uri; 67 67 use tonic::codegen::*; 68 + use tonic::codegen::http::Uri; 68 69 #[derive(Debug, Clone)] 69 70 pub struct BrowseServiceClient<T> { 70 71 inner: tonic::client::Grpc<T>, ··· 108 109 <T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody, 109 110 >, 110 111 >, 111 - <T as tonic::codegen::Service<http::Request<tonic::body::BoxBody>>>::Error: 112 - Into<StdError> + std::marker::Send + std::marker::Sync, 112 + <T as tonic::codegen::Service< 113 + http::Request<tonic::body::BoxBody>, 114 + >>::Error: Into<StdError> + std::marker::Send + std::marker::Sync, 113 115 { 114 116 BrowseServiceClient::new(InterceptedService::new(inner, interceptor)) 115 117 } ··· 147 149 pub async fn get_categories( 148 150 &mut self, 149 151 request: impl tonic::IntoRequest<super::GetCategoriesRequest>, 150 - ) -> std::result::Result<tonic::Response<super::GetCategoriesResponse>, tonic::Status> 151 - { 152 - self.inner.ready().await.map_err(|e| { 153 - tonic::Status::unknown(format!("Service was not ready: {}", e.into())) 154 - })?; 152 + ) -> std::result::Result< 153 + tonic::Response<super::GetCategoriesResponse>, 154 + tonic::Status, 155 + > { 156 + self.inner 157 + .ready() 158 + .await 159 + .map_err(|e| { 160 + tonic::Status::unknown( 161 + format!("Service was not ready: {}", e.into()), 162 + ) 163 + })?; 155 164 let codec = tonic::codec::ProstCodec::default(); 156 165 let path = http::uri::PathAndQuery::from_static( 157 166 "/tunein.v1alpha1.BrowseService/GetCategories", 158 167 ); 159 168 let mut req = request.into_request(); 160 - req.extensions_mut().insert(GrpcMethod::new( 161 - "tunein.v1alpha1.BrowseService", 162 - "GetCategories", 163 - )); 169 + req.extensions_mut() 170 + .insert( 171 + GrpcMethod::new("tunein.v1alpha1.BrowseService", "GetCategories"), 172 + ); 164 173 self.inner.unary(req, path, codec).await 165 174 } 166 175 pub async fn browse_category( 167 176 &mut self, 168 177 request: impl tonic::IntoRequest<super::BrowseCategoryRequest>, 169 - ) -> std::result::Result<tonic::Response<super::BrowseCategoryResponse>, tonic::Status> 170 - { 171 - self.inner.ready().await.map_err(|e| { 172 - tonic::Status::unknown(format!("Service was not ready: {}", e.into())) 173 - })?; 178 + ) -> std::result::Result< 179 + tonic::Response<super::BrowseCategoryResponse>, 180 + tonic::Status, 181 + > { 182 + self.inner 183 + .ready() 184 + .await 185 + .map_err(|e| { 186 + tonic::Status::unknown( 187 + format!("Service was not ready: {}", e.into()), 188 + ) 189 + })?; 174 190 let codec = tonic::codec::ProstCodec::default(); 175 191 let path = http::uri::PathAndQuery::from_static( 176 192 "/tunein.v1alpha1.BrowseService/BrowseCategory", 177 193 ); 178 194 let mut req = request.into_request(); 179 - req.extensions_mut().insert(GrpcMethod::new( 180 - "tunein.v1alpha1.BrowseService", 181 - "BrowseCategory", 182 - )); 195 + req.extensions_mut() 196 + .insert( 197 + GrpcMethod::new("tunein.v1alpha1.BrowseService", "BrowseCategory"), 198 + ); 183 199 self.inner.unary(req, path, codec).await 184 200 } 185 201 pub async fn get_station_details( 186 202 &mut self, 187 203 request: impl tonic::IntoRequest<super::GetStationDetailsRequest>, 188 - ) -> std::result::Result<tonic::Response<super::GetStationDetailsResponse>, tonic::Status> 189 - { 190 - self.inner.ready().await.map_err(|e| { 191 - tonic::Status::unknown(format!("Service was not ready: {}", e.into())) 192 - })?; 204 + ) -> std::result::Result< 205 + tonic::Response<super::GetStationDetailsResponse>, 206 + tonic::Status, 207 + > { 208 + self.inner 209 + .ready() 210 + .await 211 + .map_err(|e| { 212 + tonic::Status::unknown( 213 + format!("Service was not ready: {}", e.into()), 214 + ) 215 + })?; 193 216 let codec = tonic::codec::ProstCodec::default(); 194 217 let path = http::uri::PathAndQuery::from_static( 195 218 "/tunein.v1alpha1.BrowseService/GetStationDetails", 196 219 ); 197 220 let mut req = request.into_request(); 198 - req.extensions_mut().insert(GrpcMethod::new( 199 - "tunein.v1alpha1.BrowseService", 200 - "GetStationDetails", 201 - )); 221 + req.extensions_mut() 222 + .insert( 223 + GrpcMethod::new("tunein.v1alpha1.BrowseService", "GetStationDetails"), 224 + ); 202 225 self.inner.unary(req, path, codec).await 203 226 } 204 227 pub async fn search( 205 228 &mut self, 206 229 request: impl tonic::IntoRequest<super::SearchRequest>, 207 230 ) -> std::result::Result<tonic::Response<super::SearchResponse>, tonic::Status> { 208 - self.inner.ready().await.map_err(|e| { 209 - tonic::Status::unknown(format!("Service was not ready: {}", e.into())) 210 - })?; 231 + self.inner 232 + .ready() 233 + .await 234 + .map_err(|e| { 235 + tonic::Status::unknown( 236 + format!("Service was not ready: {}", e.into()), 237 + ) 238 + })?; 211 239 let codec = tonic::codec::ProstCodec::default(); 212 - let path = 213 - http::uri::PathAndQuery::from_static("/tunein.v1alpha1.BrowseService/Search"); 240 + let path = http::uri::PathAndQuery::from_static( 241 + "/tunein.v1alpha1.BrowseService/Search", 242 + ); 214 243 let mut req = request.into_request(); 215 244 req.extensions_mut() 216 245 .insert(GrpcMethod::new("tunein.v1alpha1.BrowseService", "Search")); ··· 225 254 dead_code, 226 255 missing_docs, 227 256 clippy::wildcard_imports, 228 - clippy::let_unit_value 257 + clippy::let_unit_value, 229 258 )] 230 259 use tonic::codegen::*; 231 260 /// Generated trait containing gRPC methods that should be implemented for use with BrowseServiceServer. ··· 234 263 async fn get_categories( 235 264 &self, 236 265 request: tonic::Request<super::GetCategoriesRequest>, 237 - ) -> std::result::Result<tonic::Response<super::GetCategoriesResponse>, tonic::Status>; 266 + ) -> std::result::Result< 267 + tonic::Response<super::GetCategoriesResponse>, 268 + tonic::Status, 269 + >; 238 270 async fn browse_category( 239 271 &self, 240 272 request: tonic::Request<super::BrowseCategoryRequest>, 241 - ) -> std::result::Result<tonic::Response<super::BrowseCategoryResponse>, tonic::Status>; 273 + ) -> std::result::Result< 274 + tonic::Response<super::BrowseCategoryResponse>, 275 + tonic::Status, 276 + >; 242 277 async fn get_station_details( 243 278 &self, 244 279 request: tonic::Request<super::GetStationDetailsRequest>, 245 - ) -> std::result::Result<tonic::Response<super::GetStationDetailsResponse>, tonic::Status>; 280 + ) -> std::result::Result< 281 + tonic::Response<super::GetStationDetailsResponse>, 282 + tonic::Status, 283 + >; 246 284 async fn search( 247 285 &self, 248 286 request: tonic::Request<super::SearchRequest>, ··· 269 307 max_encoding_message_size: None, 270 308 } 271 309 } 272 - pub fn with_interceptor<F>(inner: T, interceptor: F) -> InterceptedService<Self, F> 310 + pub fn with_interceptor<F>( 311 + inner: T, 312 + interceptor: F, 313 + ) -> InterceptedService<Self, F> 273 314 where 274 315 F: tonic::service::Interceptor, 275 316 { ··· 324 365 "/tunein.v1alpha1.BrowseService/GetCategories" => { 325 366 #[allow(non_camel_case_types)] 326 367 struct GetCategoriesSvc<T: BrowseService>(pub Arc<T>); 327 - impl<T: BrowseService> tonic::server::UnaryService<super::GetCategoriesRequest> 328 - for GetCategoriesSvc<T> 329 - { 368 + impl< 369 + T: BrowseService, 370 + > tonic::server::UnaryService<super::GetCategoriesRequest> 371 + for GetCategoriesSvc<T> { 330 372 type Response = super::GetCategoriesResponse; 331 - type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>; 373 + type Future = BoxFuture< 374 + tonic::Response<Self::Response>, 375 + tonic::Status, 376 + >; 332 377 fn call( 333 378 &mut self, 334 379 request: tonic::Request<super::GetCategoriesRequest>, ··· 365 410 "/tunein.v1alpha1.BrowseService/BrowseCategory" => { 366 411 #[allow(non_camel_case_types)] 367 412 struct BrowseCategorySvc<T: BrowseService>(pub Arc<T>); 368 - impl<T: BrowseService> tonic::server::UnaryService<super::BrowseCategoryRequest> 369 - for BrowseCategorySvc<T> 370 - { 413 + impl< 414 + T: BrowseService, 415 + > tonic::server::UnaryService<super::BrowseCategoryRequest> 416 + for BrowseCategorySvc<T> { 371 417 type Response = super::BrowseCategoryResponse; 372 - type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>; 418 + type Future = BoxFuture< 419 + tonic::Response<Self::Response>, 420 + tonic::Status, 421 + >; 373 422 fn call( 374 423 &mut self, 375 424 request: tonic::Request<super::BrowseCategoryRequest>, ··· 406 455 "/tunein.v1alpha1.BrowseService/GetStationDetails" => { 407 456 #[allow(non_camel_case_types)] 408 457 struct GetStationDetailsSvc<T: BrowseService>(pub Arc<T>); 409 - impl<T: BrowseService> 410 - tonic::server::UnaryService<super::GetStationDetailsRequest> 411 - for GetStationDetailsSvc<T> 412 - { 458 + impl< 459 + T: BrowseService, 460 + > tonic::server::UnaryService<super::GetStationDetailsRequest> 461 + for GetStationDetailsSvc<T> { 413 462 type Response = super::GetStationDetailsResponse; 414 - type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>; 463 + type Future = BoxFuture< 464 + tonic::Response<Self::Response>, 465 + tonic::Status, 466 + >; 415 467 fn call( 416 468 &mut self, 417 469 request: tonic::Request<super::GetStationDetailsRequest>, 418 470 ) -> Self::Future { 419 471 let inner = Arc::clone(&self.0); 420 472 let fut = async move { 421 - <T as BrowseService>::get_station_details(&inner, request).await 473 + <T as BrowseService>::get_station_details(&inner, request) 474 + .await 422 475 }; 423 476 Box::pin(fut) 424 477 } ··· 448 501 "/tunein.v1alpha1.BrowseService/Search" => { 449 502 #[allow(non_camel_case_types)] 450 503 struct SearchSvc<T: BrowseService>(pub Arc<T>); 451 - impl<T: BrowseService> tonic::server::UnaryService<super::SearchRequest> for SearchSvc<T> { 504 + impl< 505 + T: BrowseService, 506 + > tonic::server::UnaryService<super::SearchRequest> 507 + for SearchSvc<T> { 452 508 type Response = super::SearchResponse; 453 - type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>; 509 + type Future = BoxFuture< 510 + tonic::Response<Self::Response>, 511 + tonic::Status, 512 + >; 454 513 fn call( 455 514 &mut self, 456 515 request: tonic::Request<super::SearchRequest>, 457 516 ) -> Self::Future { 458 517 let inner = Arc::clone(&self.0); 459 - let fut = 460 - async move { <T as BrowseService>::search(&inner, request).await }; 518 + let fut = async move { 519 + <T as BrowseService>::search(&inner, request).await 520 + }; 461 521 Box::pin(fut) 462 522 } 463 523 } ··· 483 543 }; 484 544 Box::pin(fut) 485 545 } 486 - _ => Box::pin(async move { 487 - let mut response = http::Response::new(empty_body()); 488 - let headers = response.headers_mut(); 489 - headers.insert( 490 - tonic::Status::GRPC_STATUS, 491 - (tonic::Code::Unimplemented as i32).into(), 492 - ); 493 - headers.insert( 494 - http::header::CONTENT_TYPE, 495 - tonic::metadata::GRPC_CONTENT_TYPE, 496 - ); 497 - Ok(response) 498 - }), 546 + _ => { 547 + Box::pin(async move { 548 + let mut response = http::Response::new(empty_body()); 549 + let headers = response.headers_mut(); 550 + headers 551 + .insert( 552 + tonic::Status::GRPC_STATUS, 553 + (tonic::Code::Unimplemented as i32).into(), 554 + ); 555 + headers 556 + .insert( 557 + http::header::CONTENT_TYPE, 558 + tonic::metadata::GRPC_CONTENT_TYPE, 559 + ); 560 + Ok(response) 561 + }) 562 + } 499 563 } 500 564 } 501 565 } ··· 541 605 dead_code, 542 606 missing_docs, 543 607 clippy::wildcard_imports, 544 - clippy::let_unit_value 608 + clippy::let_unit_value, 545 609 )] 546 - use tonic::codegen::http::Uri; 547 610 use tonic::codegen::*; 611 + use tonic::codegen::http::Uri; 548 612 #[derive(Debug, Clone)] 549 613 pub struct PlaybackServiceClient<T> { 550 614 inner: tonic::client::Grpc<T>, ··· 588 652 <T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody, 589 653 >, 590 654 >, 591 - <T as tonic::codegen::Service<http::Request<tonic::body::BoxBody>>>::Error: 592 - Into<StdError> + std::marker::Send + std::marker::Sync, 655 + <T as tonic::codegen::Service< 656 + http::Request<tonic::body::BoxBody>, 657 + >>::Error: Into<StdError> + std::marker::Send + std::marker::Sync, 593 658 { 594 659 PlaybackServiceClient::new(InterceptedService::new(inner, interceptor)) 595 660 } ··· 628 693 &mut self, 629 694 request: impl tonic::IntoRequest<super::PlayRequest>, 630 695 ) -> std::result::Result<tonic::Response<super::PlayResponse>, tonic::Status> { 631 - self.inner.ready().await.map_err(|e| { 632 - tonic::Status::unknown(format!("Service was not ready: {}", e.into())) 633 - })?; 696 + self.inner 697 + .ready() 698 + .await 699 + .map_err(|e| { 700 + tonic::Status::unknown( 701 + format!("Service was not ready: {}", e.into()), 702 + ) 703 + })?; 634 704 let codec = tonic::codec::ProstCodec::default(); 635 - let path = 636 - http::uri::PathAndQuery::from_static("/tunein.v1alpha1.PlaybackService/Play"); 705 + let path = http::uri::PathAndQuery::from_static( 706 + "/tunein.v1alpha1.PlaybackService/Play", 707 + ); 637 708 let mut req = request.into_request(); 638 709 req.extensions_mut() 639 710 .insert(GrpcMethod::new("tunein.v1alpha1.PlaybackService", "Play")); ··· 643 714 &mut self, 644 715 request: impl tonic::IntoRequest<super::StopRequest>, 645 716 ) -> std::result::Result<tonic::Response<super::StopResponse>, tonic::Status> { 646 - self.inner.ready().await.map_err(|e| { 647 - tonic::Status::unknown(format!("Service was not ready: {}", e.into())) 648 - })?; 717 + self.inner 718 + .ready() 719 + .await 720 + .map_err(|e| { 721 + tonic::Status::unknown( 722 + format!("Service was not ready: {}", e.into()), 723 + ) 724 + })?; 649 725 let codec = tonic::codec::ProstCodec::default(); 650 - let path = 651 - http::uri::PathAndQuery::from_static("/tunein.v1alpha1.PlaybackService/Stop"); 726 + let path = http::uri::PathAndQuery::from_static( 727 + "/tunein.v1alpha1.PlaybackService/Stop", 728 + ); 652 729 let mut req = request.into_request(); 653 730 req.extensions_mut() 654 731 .insert(GrpcMethod::new("tunein.v1alpha1.PlaybackService", "Stop")); ··· 657 734 pub async fn play_or_pause( 658 735 &mut self, 659 736 request: impl tonic::IntoRequest<super::PlayOrPauseRequest>, 660 - ) -> std::result::Result<tonic::Response<super::PlayOrPauseResponse>, tonic::Status> 661 - { 662 - self.inner.ready().await.map_err(|e| { 663 - tonic::Status::unknown(format!("Service was not ready: {}", e.into())) 664 - })?; 737 + ) -> std::result::Result< 738 + tonic::Response<super::PlayOrPauseResponse>, 739 + tonic::Status, 740 + > { 741 + self.inner 742 + .ready() 743 + .await 744 + .map_err(|e| { 745 + tonic::Status::unknown( 746 + format!("Service was not ready: {}", e.into()), 747 + ) 748 + })?; 665 749 let codec = tonic::codec::ProstCodec::default(); 666 750 let path = http::uri::PathAndQuery::from_static( 667 751 "/tunein.v1alpha1.PlaybackService/PlayOrPause", 668 752 ); 669 753 let mut req = request.into_request(); 670 - req.extensions_mut().insert(GrpcMethod::new( 671 - "tunein.v1alpha1.PlaybackService", 672 - "PlayOrPause", 673 - )); 754 + req.extensions_mut() 755 + .insert( 756 + GrpcMethod::new("tunein.v1alpha1.PlaybackService", "PlayOrPause"), 757 + ); 674 758 self.inner.unary(req, path, codec).await 675 759 } 676 760 } ··· 682 766 dead_code, 683 767 missing_docs, 684 768 clippy::wildcard_imports, 685 - clippy::let_unit_value 769 + clippy::let_unit_value, 686 770 )] 687 771 use tonic::codegen::*; 688 772 /// Generated trait containing gRPC methods that should be implemented for use with PlaybackServiceServer. ··· 699 783 async fn play_or_pause( 700 784 &self, 701 785 request: tonic::Request<super::PlayOrPauseRequest>, 702 - ) -> std::result::Result<tonic::Response<super::PlayOrPauseResponse>, tonic::Status>; 786 + ) -> std::result::Result< 787 + tonic::Response<super::PlayOrPauseResponse>, 788 + tonic::Status, 789 + >; 703 790 } 704 791 #[derive(Debug)] 705 792 pub struct PlaybackServiceServer<T> { ··· 722 809 max_encoding_message_size: None, 723 810 } 724 811 } 725 - pub fn with_interceptor<F>(inner: T, interceptor: F) -> InterceptedService<Self, F> 812 + pub fn with_interceptor<F>( 813 + inner: T, 814 + interceptor: F, 815 + ) -> InterceptedService<Self, F> 726 816 where 727 817 F: tonic::service::Interceptor, 728 818 { ··· 777 867 "/tunein.v1alpha1.PlaybackService/Play" => { 778 868 #[allow(non_camel_case_types)] 779 869 struct PlaySvc<T: PlaybackService>(pub Arc<T>); 780 - impl<T: PlaybackService> tonic::server::UnaryService<super::PlayRequest> for PlaySvc<T> { 870 + impl< 871 + T: PlaybackService, 872 + > tonic::server::UnaryService<super::PlayRequest> for PlaySvc<T> { 781 873 type Response = super::PlayResponse; 782 - type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>; 874 + type Future = BoxFuture< 875 + tonic::Response<Self::Response>, 876 + tonic::Status, 877 + >; 783 878 fn call( 784 879 &mut self, 785 880 request: tonic::Request<super::PlayRequest>, 786 881 ) -> Self::Future { 787 882 let inner = Arc::clone(&self.0); 788 - let fut = 789 - async move { <T as PlaybackService>::play(&inner, request).await }; 883 + let fut = async move { 884 + <T as PlaybackService>::play(&inner, request).await 885 + }; 790 886 Box::pin(fut) 791 887 } 792 888 } ··· 815 911 "/tunein.v1alpha1.PlaybackService/Stop" => { 816 912 #[allow(non_camel_case_types)] 817 913 struct StopSvc<T: PlaybackService>(pub Arc<T>); 818 - impl<T: PlaybackService> tonic::server::UnaryService<super::StopRequest> for StopSvc<T> { 914 + impl< 915 + T: PlaybackService, 916 + > tonic::server::UnaryService<super::StopRequest> for StopSvc<T> { 819 917 type Response = super::StopResponse; 820 - type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>; 918 + type Future = BoxFuture< 919 + tonic::Response<Self::Response>, 920 + tonic::Status, 921 + >; 821 922 fn call( 822 923 &mut self, 823 924 request: tonic::Request<super::StopRequest>, 824 925 ) -> Self::Future { 825 926 let inner = Arc::clone(&self.0); 826 - let fut = 827 - async move { <T as PlaybackService>::stop(&inner, request).await }; 927 + let fut = async move { 928 + <T as PlaybackService>::stop(&inner, request).await 929 + }; 828 930 Box::pin(fut) 829 931 } 830 932 } ··· 853 955 "/tunein.v1alpha1.PlaybackService/PlayOrPause" => { 854 956 #[allow(non_camel_case_types)] 855 957 struct PlayOrPauseSvc<T: PlaybackService>(pub Arc<T>); 856 - impl<T: PlaybackService> tonic::server::UnaryService<super::PlayOrPauseRequest> 857 - for PlayOrPauseSvc<T> 858 - { 958 + impl< 959 + T: PlaybackService, 960 + > tonic::server::UnaryService<super::PlayOrPauseRequest> 961 + for PlayOrPauseSvc<T> { 859 962 type Response = super::PlayOrPauseResponse; 860 - type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>; 963 + type Future = BoxFuture< 964 + tonic::Response<Self::Response>, 965 + tonic::Status, 966 + >; 861 967 fn call( 862 968 &mut self, 863 969 request: tonic::Request<super::PlayOrPauseRequest>, ··· 891 997 }; 892 998 Box::pin(fut) 893 999 } 894 - _ => Box::pin(async move { 895 - let mut response = http::Response::new(empty_body()); 896 - let headers = response.headers_mut(); 897 - headers.insert( 898 - tonic::Status::GRPC_STATUS, 899 - (tonic::Code::Unimplemented as i32).into(), 900 - ); 901 - headers.insert( 902 - http::header::CONTENT_TYPE, 903 - tonic::metadata::GRPC_CONTENT_TYPE, 904 - ); 905 - Ok(response) 906 - }), 1000 + _ => { 1001 + Box::pin(async move { 1002 + let mut response = http::Response::new(empty_body()); 1003 + let headers = response.headers_mut(); 1004 + headers 1005 + .insert( 1006 + tonic::Status::GRPC_STATUS, 1007 + (tonic::Code::Unimplemented as i32).into(), 1008 + ); 1009 + headers 1010 + .insert( 1011 + http::header::CONTENT_TYPE, 1012 + tonic::metadata::GRPC_CONTENT_TYPE, 1013 + ); 1014 + Ok(response) 1015 + }) 1016 + } 907 1017 } 908 1018 } 909 1019 }
+1 -1
src/app.rs
··· 887 887 } 888 888 889 889 /// Send [`os_media_controls::Command`]. 890 - fn send_os_media_controls_command( 890 + pub fn send_os_media_controls_command( 891 891 os_media_controls: Option<&mut OsMediaControls>, 892 892 command: os_media_controls::Command<'_>, 893 893 ) {
+47 -1
src/interactive.rs
··· 7 7 use ratatui::prelude::*; 8 8 use ratatui::widgets::{Block, Borders, List, ListItem, ListState, Paragraph}; 9 9 use tokio::sync::mpsc; 10 + use tunein_cli::os_media_controls::{self, OsMediaControls}; 10 11 12 + use crate::app::send_os_media_controls_command; 11 13 use crate::audio::{AudioController, PlaybackEvent, PlaybackState}; 12 14 use crate::extract::get_currently_playing; 13 15 use crate::favorites::{FavoriteStation, FavoritesStore}; ··· 42 44 let (input_tx, mut input_rx) = mpsc::unbounded_channel(); 43 45 spawn_input_thread(input_tx.clone()); 44 46 47 + let os_media_controls = OsMediaControls::new() 48 + .inspect_err(|err| { 49 + eprintln!( 50 + "error: failed to initialize os media controls due to `{}`", 51 + err 52 + ); 53 + }) 54 + .ok(); 55 + 45 56 let mut app = HubApp::new( 46 57 provider_name.to_string(), 47 58 provider, 48 59 audio, 49 60 favorites, 50 61 metadata_tx, 62 + os_media_controls, 51 63 ); 52 64 53 65 let result = loop { ··· 103 115 metadata_tx: mpsc::UnboundedSender<HubMessage>, 104 116 now_playing_station_id: Option<String>, 105 117 next_now_playing_poll: Instant, 118 + os_media_controls: Option<OsMediaControls>, 106 119 } 107 120 108 121 impl HubApp { ··· 112 125 audio: AudioController, 113 126 favorites: FavoritesStore, 114 127 metadata_tx: mpsc::UnboundedSender<HubMessage>, 128 + os_media_controls: Option<OsMediaControls>, 115 129 ) -> Self { 116 130 let mut ui = UiState::default(); 117 131 ui.menu_state.select(Some(0)); ··· 129 143 metadata_tx, 130 144 now_playing_station_id: None, 131 145 next_now_playing_poll: Instant::now(), 146 + os_media_controls, 132 147 } 133 148 } 134 149 ··· 202 217 }) 203 218 }) 204 219 .unwrap_or_else(|| "Unknown".to_string()); 205 - self.render_labeled_line(frame, area, row, "Station ", &station_name); 220 + let station_id = self 221 + .current_playback 222 + .as_ref() 223 + .map(|p| p.station.id.as_str()) 224 + .or_else(|| self.current_station.as_ref().map(|s| s.station.id.as_str())) 225 + .unwrap_or("N/A"); 226 + 227 + self.render_labeled_line( 228 + frame, 229 + area, 230 + row, 231 + "Station ", 232 + &format!("{} - {}", station_name, station_id), 233 + ); 206 234 row += 1; 207 235 208 236 let now_playing = self ··· 969 997 self.current_playback = Some(state.clone()); 970 998 if let Some(station) = self.current_station.as_mut() { 971 999 station.station.playing = Some(state.now_playing.clone()); 1000 + station.station.id = state.station.id.clone(); 972 1001 } 973 1002 self.set_status(&format!("Now playing {}", state.stream_name)); 974 1003 self.prepare_now_playing_poll(); ··· 996 1025 station.station.playing = Some(now_playing.clone()); 997 1026 } 998 1027 self.set_status(&format!("Now Playing {}", now_playing)); 1028 + 1029 + let name = self 1030 + .current_station 1031 + .as_ref() 1032 + .map(|s| s.station.name.clone()) 1033 + .unwrap_or_default(); 1034 + 1035 + send_os_media_controls_command( 1036 + self.os_media_controls.as_mut(), 1037 + os_media_controls::Command::SetMetadata(souvlaki::MediaMetadata { 1038 + title: (!now_playing.is_empty()).then_some(now_playing.as_str()), 1039 + album: (!name.is_empty()).then_some(name.as_str()), 1040 + artist: None, 1041 + cover_url: None, 1042 + duration: None, 1043 + }), 1044 + ); 999 1045 } 1000 1046 } 1001 1047 }
+6 -1
src/provider/tunein.rs
··· 55 55 None => Ok(None), 56 56 } 57 57 } 58 - _ => Ok(Some(Station::from(stations[0].clone()))), 58 + _ => { 59 + let mut station = Station::from(stations[0].clone()); 60 + // Preserve the original station ID since StationLinkDetails doesn't contain it 61 + station.id = id; 62 + Ok(Some(station)) 63 + } 59 64 } 60 65 } 61 66