-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add CLI * Remove debug * Trim ws * Setup and README * typo fix
- Loading branch information
Showing
8 changed files
with
245 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ dist | |
.vscode | ||
.theia | ||
.venv/ | ||
venv/ | ||
|
||
# Created by unit tests | ||
.pytest_cache/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,21 @@ | ||
# TuneIn | ||
|
||
unnoficial python api for TuneIn | ||
unnoficial python api for TuneIn | ||
|
||
## Usage | ||
|
||
### From the command line | ||
|
||
tunein comes with a basic command line interface for searching. Output is availbe in both `json`` and table formats with the default being the later. | ||
|
||
Example: | ||
|
||
``` | ||
tunein search "Radio paradise" | ||
``` | ||
|
||
``` | ||
tunein search "Radio paradise" --format json | ||
``` | ||
|
||
Command line help is available with `tunein --help` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
"""The CLI entrypoint for tunein.""" | ||
|
||
import argparse | ||
|
||
from tunein import subcommands | ||
|
||
class Cli: | ||
"""The CLI entrypoint for tunein.""" | ||
|
||
def __init__(self) -> None: | ||
"""Initialize the CLI entrypoint.""" | ||
self._args: argparse.Namespace | ||
|
||
def parse_args(self) -> None: | ||
"""Parse the command line arguments.""" | ||
parser = argparse.ArgumentParser( | ||
description="unnoficial python api for TuneIn.", | ||
) | ||
|
||
subparsers = parser.add_subparsers( | ||
title="Commands", | ||
dest="subcommand", | ||
metavar="", | ||
required=True, | ||
) | ||
|
||
search = subparsers.add_parser( | ||
"search", | ||
help="Search tunein for stations", | ||
) | ||
|
||
search.add_argument( | ||
"station", | ||
help="Station to search for", | ||
) | ||
|
||
search.add_argument( | ||
"-f", "--format", | ||
choices=["json", "table"], | ||
default="table", | ||
help="Output format", | ||
) | ||
|
||
self._args = parser.parse_args() | ||
|
||
def run(self) -> None: | ||
"""Run the CLI.""" | ||
subcommand_cls = getattr(subcommands, self._args.subcommand.capitalize()) | ||
subcommand = subcommand_cls(args=self._args) | ||
subcommand.run() | ||
|
||
def main() -> None: | ||
"""Run the CLI.""" | ||
cli = Cli() | ||
cli.parse_args() | ||
cli.run() | ||
|
||
|
||
if __name__ == "__main__": | ||
main() | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
"""The subcommands for tunein.""" | ||
|
||
from .search import Search |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
"""The search subcommand for tunein.""" | ||
|
||
from __future__ import annotations | ||
|
||
import argparse | ||
import json | ||
import shutil | ||
import sys | ||
|
||
from tunein import TuneIn | ||
|
||
class Ansi: | ||
"""ANSI escape codes.""" | ||
|
||
BLUE = "\x1B[34m" | ||
BOLD = "\x1B[1m" | ||
CYAN = "\x1B[36m" | ||
GREEN = "\x1B[32m" | ||
ITALIC = "\x1B[3m" | ||
MAGENTA = "\x1B[35m" | ||
RED = "\x1B[31m" | ||
RESET = "\x1B[0m" | ||
REVERSED = "\x1B[7m" | ||
UNDERLINE = "\x1B[4m" | ||
WHITE = "\x1B[37m" | ||
YELLOW = "\x1B[33m" | ||
GREY = "\x1B[90m" | ||
|
||
NOPRINT_TRANS_TABLE = { | ||
i: None for i in range(0, sys.maxunicode + 1) if not chr(i).isprintable() | ||
} | ||
|
||
class Search: | ||
"""The search subcommand for tunein.""" | ||
|
||
def __init__(self: Search, args: argparse.Namespace) -> None: | ||
"""Initialize the search subcommand.""" | ||
self._args: argparse.Namespace = args | ||
|
||
def run(self: Search) -> None: | ||
"""Run the search subcommand.""" | ||
tunein = TuneIn() | ||
results = tunein.search(self._args.station) | ||
stations = [station.dict for station in results] | ||
if not stations: | ||
print(f"No results for {self._args.station}") | ||
sys.exit(1) | ||
stations.sort(key=lambda x: x["match"], reverse=True) | ||
for station in stations: | ||
station["title"] = self._printable(station["title"]) | ||
station["artist"] = self._printable(station["artist"]) | ||
station["description"] = self._printable(station["description"]) | ||
|
||
if self._args.format == "json": | ||
print(json.dumps(stations, indent=4)) | ||
elif self._args.format == "table": | ||
max_widths = {} | ||
columns = ["title", "artist", "description"] | ||
for column in columns: | ||
max_width = max(len(str(station[column])) for station in stations) | ||
if column == "description": | ||
term_width = shutil.get_terminal_size().columns | ||
remaining = term_width - max_widths["title"] - max_widths["artist"] - 2 | ||
max_width = min(max_width, remaining) | ||
max_widths[column] = max_width | ||
|
||
print(" ".join(column.ljust(max_widths[column]).capitalize() for column in columns)) | ||
print(" ".join("-" * max_widths[column] for column in columns)) | ||
for station in stations: | ||
line_parts = [] | ||
# title as link | ||
link = self._term_link(station.get("stream"), station["title"]) | ||
line_parts.append(f"{link}{' '*(max_widths['title']-len(station['title']))}") | ||
# artist | ||
line_parts.append(str(station["artist"]).ljust(max_widths["artist"])) | ||
# description clipped | ||
line_parts.append(str(station["description"])[:max_widths["description"]]) | ||
print(" ".join(line_parts)) | ||
|
||
@staticmethod | ||
def _term_link(uri: str, label: str) -> str: | ||
"""Return a link. | ||
Args: | ||
uri: The URI to link to | ||
label: The label to use for the link | ||
Returns: | ||
The link | ||
""" | ||
parameters = "" | ||
|
||
# OSC 8 ; params ; URI ST <name> OSC 8 ;; ST | ||
escape_mask = "\x1b]8;{};{}\x1b\\{}\x1b]8;;\x1b\\" | ||
link_str = escape_mask.format(parameters, uri, label) | ||
return f"{Ansi.BLUE}{link_str}{Ansi.RESET}" | ||
|
||
@staticmethod | ||
def _printable(string: str) -> str: | ||
"""Replace non-printable characters in a string. | ||
Args: | ||
string: The string to replace non-printable characters in. | ||
Returns: | ||
The string with non-printable characters replaced. | ||
""" | ||
return string.translate(NOPRINT_TRANS_TABLE) | ||
|
||
|