···11+# Contributor Covenant Code of Conduct
22+33+## Our Pledge
44+55+We as members, contributors, and leaders pledge to make participation in our
66+community a harassment-free experience for everyone, regardless of age, body
77+size, visible or invisible disability, ethnicity, sex characteristics, gender
88+identity and expression, level of experience, education, socio-economic status,
99+nationality, personal appearance, race, caste, color, religion, or sexual
1010+identity and orientation.
1111+1212+We pledge to act and interact in ways that contribute to an open, welcoming,
1313+diverse, inclusive, and healthy community.
1414+1515+## Our Standards
1616+1717+Examples of behavior that contributes to a positive environment for our
1818+community include:
1919+2020+* Demonstrating empathy and kindness toward other people
2121+* Being respectful of differing opinions, viewpoints, and experiences
2222+* Giving and gracefully accepting constructive feedback
2323+* Accepting responsibility and apologizing to those affected by our mistakes,
2424+ and learning from the experience
2525+* Focusing on what is best not just for us as individuals, but for the overall
2626+ community
2727+2828+Examples of unacceptable behavior include:
2929+3030+* The use of sexualized language or imagery, and sexual attention or advances of
3131+ any kind
3232+* Trolling, insulting or derogatory comments, and personal or political attacks
3333+* Public or private harassment
3434+* Publishing others' private information, such as a physical or email address,
3535+ without their explicit permission
3636+* Other conduct which could reasonably be considered inappropriate in a
3737+ professional setting
3838+3939+## Enforcement Responsibilities
4040+4141+Community leaders are responsible for clarifying and enforcing our standards of
4242+acceptable behavior and will take appropriate and fair corrective action in
4343+response to any behavior that they deem inappropriate, threatening, offensive,
4444+or harmful.
4545+4646+Community leaders have the right and responsibility to remove, edit, or reject
4747+comments, commits, code, wiki edits, issues, and other contributions that are
4848+not aligned to this Code of Conduct, and will communicate reasons for moderation
4949+decisions when appropriate.
5050+5151+## Scope
5252+5353+This Code of Conduct applies within all community spaces, and also applies when
5454+an individual is officially representing the community in public spaces.
5555+Examples of representing our community include using an official e-mail address,
5656+posting via an official social media account, or acting as an appointed
5757+representative at an online or offline event.
5858+5959+## Enforcement
6060+6161+Instances of abusive, harassing, or otherwise unacceptable behavior may be
6262+reported to the community leaders responsible for enforcement at
6363+[GitHub Issues](https://github.com/tsirysndr/tunein-cli/issues).
6464+All complaints will be reviewed and investigated promptly and fairly.
6565+6666+All community leaders are obligated to respect the privacy and security of the
6767+reporter of any incident.
6868+6969+## Enforcement Guidelines
7070+7171+Community leaders will follow these Community Impact Guidelines in determining
7272+the consequences for any action they deem in violation of this Code of Conduct:
7373+7474+### 1. Correction
7575+7676+**Community Impact**: Use of inappropriate language or other behavior deemed
7777+unprofessional or unwelcome in the community.
7878+7979+**Consequence**: A private, written warning from community leaders, providing
8080+clarity around the nature of the violation and an explanation of why the
8181+behavior was inappropriate. A public apology may be requested.
8282+8383+### 2. Warning
8484+8585+**Community Impact**: A violation through a single incident or series of
8686+actions.
8787+8888+**Consequence**: A warning with consequences for continued behavior. No
8989+interaction with the people involved, including unsolicited interaction with
9090+those enforcing the Code of Conduct, for a specified period of time. This
9191+includes avoiding interactions in community spaces as well as external channels
9292+like social media. Violating these terms may lead to a temporary or permanent
9393+ban.
9494+9595+### 3. Temporary Ban
9696+9797+**Community Impact**: A serious violation of community standards, including
9898+sustained inappropriate behavior.
9999+100100+**Consequence**: A temporary ban from any sort of interaction or public
101101+communication with the community for a specified period of time. No public or
102102+private interaction with the people involved, including unsolicited interaction
103103+with those enforcing the Code of Conduct, is allowed during this period.
104104+Violating these terms may lead to a permanent ban.
105105+106106+### 4. Permanent Ban
107107+108108+**Community Impact**: Demonstrating a pattern of violation of community
109109+standards, including sustained inappropriate behavior, harassment of an
110110+individual, or aggression toward or disparagement of classes of individuals.
111111+112112+**Consequence**: A permanent ban from any sort of public interaction within the
113113+community.
114114+115115+## Attribution
116116+117117+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118118+version 2.1, available at
119119+[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
120120+121121+Community Impact Guidelines were inspired by
122122+[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
123123+124124+For answers to common questions about this code of conduct, see the FAQ at
125125+[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
126126+[https://www.contributor-covenant.org/translations][translations].
127127+128128+[homepage]: https://www.contributor-covenant.org
129129+[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
130130+[Mozilla CoC]: https://github.com/mozilla/diversity
131131+[FAQ]: https://www.contributor-covenant.org/faq
132132+[translations]: https://www.contributor-covenant.org/translations
+53
CONTRIBUTING.md
···11+# Contributing Guidelines
22+33+Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
44+documentation, we greatly value feedback and contributions from our community.
55+66+Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
77+information to effectively respond to your bug report or contribution.
88+99+1010+## Reporting Bugs/Feature Requests
1111+1212+We welcome you to use the GitHub issue tracker to report bugs or suggest features.
1313+1414+When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already
1515+reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
1616+1717+* A reproducible test case or series of steps
1818+* The version of our code being used
1919+* Any modifications you've made relevant to the bug
2020+* Anything unusual about your environment or deployment
2121+2222+2323+## Contributing via Pull Requests
2424+Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
2525+2626+1. You are working against the latest source on the *master* branch.
2727+2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
2828+3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
2929+3030+To send us a pull request, please:
3131+3232+1. Fork the repository.
3333+2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
3434+3. Ensure local tests pass.
3535+4. Commit to your fork using clear commit messages.
3636+5. Send us a pull request, answering any default questions in the pull request interface.
3737+6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
3838+3939+GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
4040+[creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
4141+4242+4343+## Finding contributions to work on
4444+Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start.
4545+4646+4747+## Code of Conduct
4848+This project has adopted the [Contributor Covenant](https://www.contributor-covenant.org/), version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
4949+5050+5151+## Licensing
5252+5353+See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
···11+[package]
22+name = "tunein-cli"
33+version = "0.1.0"
44+edition = "2021"
55+66+[[bin]]
77+name = "tunein"
88+path = "src/main.rs"
99+1010+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1111+1212+[dependencies]
1313+anyhow = "1.0.69"
1414+clap = "3.2.20"
1515+owo-colors = "3.5.0"
1616+tunein = "0.1.1"
1717+tokio = { version = "1.24.2", features = ["tokio-macros", "macros", "rt", "rt-multi-thread"] }
+19
LICENSE
···11+Copyright (c) 2023 Tsiry Sandratraina <tsiry.sndr@aol.com>
22+33+Permission is hereby granted, free of charge, to any person obtaining a copy
44+of this software and associated documentation files (the "Software"), to deal
55+in the Software without restriction, including without limitation the rights
66+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
77+copies of the Software, and to permit persons to whom the Software is
88+furnished to do so, subject to the following conditions:
99+1010+The above copyright notice and this permission notice shall be included in all
1111+copies or substantial portions of the Software.
1212+1313+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1414+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1515+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1616+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1717+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1818+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1919+SOFTWARE.
+30
README.md
···11+# TuneIn CLI
22+33+A command line interface for [TuneIn Radio](https://tunein.com).<br />
44+You can search for stations, play them, and see what's currently playing.
55+66+## ๐ Installation
77+```bash
88+git clone https://github.com/tsirysndr/tunein-cli
99+cd tunein-cli
1010+cargo install --path .
1111+```
1212+1313+## ๐ Usage
1414+```
1515+USAGE:
1616+ tunein <SUBCOMMAND>
1717+1818+OPTIONS:
1919+ -h, --help Print help information
2020+ -V, --version Print version information
2121+2222+SUBCOMMANDS:
2323+ browse Browse radio stations
2424+ help Print this message or the help of the given subcommand(s)
2525+ play Play a radio station
2626+ search Search for a radio station
2727+```
2828+2929+## ๐ License
3030+[MIT](LICENSE)
+75
src/browse.rs
···11+use std::str::FromStr;
22+33+use anyhow::Error;
44+use owo_colors::OwoColorize;
55+use tunein::{types::Category, TuneInClient};
66+77+pub async fn exec(category: Option<&str>) -> Result<(), Error> {
88+ let client = TuneInClient::new();
99+1010+ if category.is_some() && Category::from_str(category.unwrap_or_default()).is_err() {
1111+ let id = category.unwrap_or_default();
1212+ let results = client
1313+ .browse_by_id(id)
1414+ .await
1515+ .map_err(|e| Error::msg(e.to_string()))?;
1616+ for result in results {
1717+ println!("{}", result.text);
1818+ if let Some(children) = result.children {
1919+ for child in children {
2020+ match child.playing {
2121+ Some(playing) => println!(" {} | {}", child.text.magenta(), playing),
2222+ None => println!(" {} | {}", child.text.magenta(), child.url.unwrap()),
2323+ }
2424+ }
2525+ }
2626+ }
2727+ return Ok(());
2828+ }
2929+3030+ let results = match category {
3131+ Some(category) => match Category::from_str(category) {
3232+ Ok(category) => client
3333+ .browse(Some(category))
3434+ .await
3535+ .map_err(|e| Error::msg(e.to_string()))?,
3636+ Err(_) => {
3737+ println!("Invalid category");
3838+ return Ok(());
3939+ }
4040+ },
4141+ None => client
4242+ .browse(None)
4343+ .await
4444+ .map_err(|e| Error::msg(e.to_string()))?,
4545+ };
4646+4747+ for result in results {
4848+ match result.guide_id {
4949+ Some(_) => println!(
5050+ "{} | id: {}",
5151+ result.text.magenta(),
5252+ result.guide_id.unwrap()
5353+ ),
5454+ None => println!("{}", result.text),
5555+ }
5656+ if let Some(children) = result.children {
5757+ for child in children {
5858+ match child.playing {
5959+ Some(playing) => println!(
6060+ " {} | {} | id: {}",
6161+ child.text.magenta(),
6262+ playing,
6363+ child.guide_id.unwrap()
6464+ ),
6565+ None => println!(
6666+ " {} | id: {}",
6767+ child.text.magenta(),
6868+ child.guide_id.unwrap()
6969+ ),
7070+ }
7171+ }
7272+ }
7373+ }
7474+ Ok(())
7575+}
+61
src/main.rs
···11+use anyhow::Error;
22+use clap::{arg, Command};
33+44+mod browse;
55+mod play;
66+mod search;
77+88+fn cli() -> Command<'static> {
99+ const VESRION: &str = env!("CARGO_PKG_VERSION");
1010+ Command::new("tunein")
1111+ .version(VESRION)
1212+ .author("Tsiry Sandratraina <tsiry.sndr@aol.com>")
1313+ .about(
1414+ r#"
1515+ ______ ____ _______ ____
1616+ /_ __/_ _____ ___ / _/__ / ___/ / / _/
1717+ / / / // / _ \/ -_)/ // _ \ / /__/ /___/ /
1818+ /_/ \_,_/_//_/\__/___/_//_/ \___/____/___/
1919+2020+A simple CLI to listen to radio stations"#,
2121+ )
2222+ .subcommand_required(true)
2323+ .subcommand(
2424+ Command::new("search")
2525+ .about("Search for a radio station")
2626+ .arg(arg!(<query> "The query to search for")),
2727+ )
2828+ .subcommand(
2929+ Command::new("play")
3030+ .about("Play a radio station")
3131+ .arg(arg!(<station> "The station to play")),
3232+ )
3333+ .subcommand(
3434+ Command::new("browse")
3535+ .about("Browse radio stations")
3636+ .arg(arg!([category] "The category (category name or id) to browse")),
3737+ )
3838+}
3939+4040+#[tokio::main]
4141+async fn main() -> Result<(), Error> {
4242+ let matches = cli().get_matches();
4343+4444+ match matches.subcommand() {
4545+ Some(("search", args)) => {
4646+ let query = args.value_of("query").unwrap();
4747+ search::exec(query).await?;
4848+ }
4949+ Some(("play", args)) => {
5050+ let station = args.value_of("station").unwrap();
5151+ play::exec(station).await?;
5252+ }
5353+ Some(("browse", args)) => {
5454+ let category = args.value_of("category");
5555+ browse::exec(category).await?;
5656+ }
5757+ _ => unreachable!(),
5858+ }
5959+6060+ Ok(())
6161+}