tisit-cli 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tisit_cli/__init__.py +2 -0
- tisit_cli/auth.py +54 -0
- tisit_cli/client.py +392 -0
- tisit_cli/commands/__init__.py +0 -0
- tisit_cli/commands/article_commands.py +132 -0
- tisit_cli/commands/auth_commands.py +91 -0
- tisit_cli/commands/book_commands.py +186 -0
- tisit_cli/commands/browse_commands.py +76 -0
- tisit_cli/commands/chat_commands.py +94 -0
- tisit_cli/commands/focus_commands.py +198 -0
- tisit_cli/commands/graph_commands.py +126 -0
- tisit_cli/commands/note_commands.py +131 -0
- tisit_cli/commands/paper_commands.py +134 -0
- tisit_cli/commands/patent_commands.py +138 -0
- tisit_cli/commands/podcast_commands.py +132 -0
- tisit_cli/commands/radar_commands.py +246 -0
- tisit_cli/commands/search_commands.py +42 -0
- tisit_cli/commands/status_commands.py +50 -0
- tisit_cli/commands/tweet_commands.py +132 -0
- tisit_cli/commands/video_commands.py +132 -0
- tisit_cli/config.py +53 -0
- tisit_cli/display.py +582 -0
- tisit_cli/exceptions.py +29 -0
- tisit_cli/main.py +68 -0
- tisit_cli-0.1.0.dist-info/METADATA +114 -0
- tisit_cli-0.1.0.dist-info/RECORD +29 -0
- tisit_cli-0.1.0.dist-info/WHEEL +4 -0
- tisit_cli-0.1.0.dist-info/entry_points.txt +2 -0
- tisit_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Note commands: add, list, view, delete."""
|
|
2
|
+
import json
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from ..auth import get_token
|
|
8
|
+
from ..client import TisitClient
|
|
9
|
+
from ..config import Config
|
|
10
|
+
from ..display import (
|
|
11
|
+
print_error, print_note_detail, print_note_table,
|
|
12
|
+
print_queued, print_success,
|
|
13
|
+
)
|
|
14
|
+
from ..exceptions import AuthenticationError, APIError, NotFoundError
|
|
15
|
+
|
|
16
|
+
note_app = typer.Typer(help="Manage your learning notes")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _get_client() -> TisitClient:
|
|
20
|
+
token = get_token()
|
|
21
|
+
if not token:
|
|
22
|
+
print_error("Not logged in. Run: tisit login")
|
|
23
|
+
raise typer.Exit(code=1)
|
|
24
|
+
cfg = Config()
|
|
25
|
+
return TisitClient(cfg.api_url, token)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@note_app.command("list")
|
|
29
|
+
def note_list(
|
|
30
|
+
query: Optional[str] = typer.Option(None, "--query", "-q", help="Search term"),
|
|
31
|
+
category: Optional[str] = typer.Option(None, "--category", "-c"),
|
|
32
|
+
domain: Optional[str] = typer.Option(None, "--domain", "-d"),
|
|
33
|
+
sort: str = typer.Option("created_at", "--sort", "-s",
|
|
34
|
+
help="Sort by: created_at, term, category"),
|
|
35
|
+
order: str = typer.Option("desc", "--order", "-o", help="asc or desc"),
|
|
36
|
+
page: int = typer.Option(1, "--page", "-p"),
|
|
37
|
+
per_page: int = typer.Option(20, "--per-page"),
|
|
38
|
+
output_json: bool = typer.Option(False, "--json", help="JSON output"),
|
|
39
|
+
):
|
|
40
|
+
"""List your notes."""
|
|
41
|
+
client = _get_client()
|
|
42
|
+
try:
|
|
43
|
+
notes, meta = client.list_notes(
|
|
44
|
+
q=query, category=category, domain=domain,
|
|
45
|
+
sort=sort, order=order, page=page, per_page=per_page,
|
|
46
|
+
)
|
|
47
|
+
except (AuthenticationError, APIError) as exc:
|
|
48
|
+
print_error(str(exc))
|
|
49
|
+
raise typer.Exit(code=1)
|
|
50
|
+
finally:
|
|
51
|
+
client.close()
|
|
52
|
+
|
|
53
|
+
if output_json:
|
|
54
|
+
typer.echo(json.dumps({"data": notes, "meta": meta}, indent=2, default=str))
|
|
55
|
+
else:
|
|
56
|
+
if not notes:
|
|
57
|
+
print_error("No notes found.")
|
|
58
|
+
else:
|
|
59
|
+
print_note_table(notes, meta)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@note_app.command("view")
|
|
63
|
+
def note_view(
|
|
64
|
+
note_id: int = typer.Argument(..., help="Note ID"),
|
|
65
|
+
output_json: bool = typer.Option(False, "--json", help="JSON output"),
|
|
66
|
+
):
|
|
67
|
+
"""View a note in detail."""
|
|
68
|
+
client = _get_client()
|
|
69
|
+
try:
|
|
70
|
+
note = client.get_note(note_id)
|
|
71
|
+
except NotFoundError:
|
|
72
|
+
print_error(f"Note {note_id} not found.")
|
|
73
|
+
raise typer.Exit(code=1)
|
|
74
|
+
except (AuthenticationError, APIError) as exc:
|
|
75
|
+
print_error(str(exc))
|
|
76
|
+
raise typer.Exit(code=1)
|
|
77
|
+
finally:
|
|
78
|
+
client.close()
|
|
79
|
+
|
|
80
|
+
if output_json:
|
|
81
|
+
typer.echo(json.dumps(note, indent=2, default=str))
|
|
82
|
+
else:
|
|
83
|
+
print_note_detail(note)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@note_app.command("add")
|
|
87
|
+
def note_add(
|
|
88
|
+
term: str = typer.Argument(..., help="Term to learn about"),
|
|
89
|
+
context: str = typer.Argument(..., help="Context for the term"),
|
|
90
|
+
output_json: bool = typer.Option(False, "--json", help="JSON output"),
|
|
91
|
+
):
|
|
92
|
+
"""Create a new note (async — queued for processing)."""
|
|
93
|
+
client = _get_client()
|
|
94
|
+
try:
|
|
95
|
+
data = client.create_note(term, context)
|
|
96
|
+
except (AuthenticationError, APIError) as exc:
|
|
97
|
+
print_error(str(exc))
|
|
98
|
+
raise typer.Exit(code=1)
|
|
99
|
+
finally:
|
|
100
|
+
client.close()
|
|
101
|
+
|
|
102
|
+
if output_json:
|
|
103
|
+
typer.echo(json.dumps(data, indent=2, default=str))
|
|
104
|
+
else:
|
|
105
|
+
print_queued(data)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@note_app.command("delete")
|
|
109
|
+
def note_delete(
|
|
110
|
+
note_id: int = typer.Argument(..., help="Note ID to delete"),
|
|
111
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation"),
|
|
112
|
+
):
|
|
113
|
+
"""Delete a note."""
|
|
114
|
+
if not yes:
|
|
115
|
+
confirm = typer.confirm(f"Delete note {note_id}?")
|
|
116
|
+
if not confirm:
|
|
117
|
+
raise typer.Abort()
|
|
118
|
+
|
|
119
|
+
client = _get_client()
|
|
120
|
+
try:
|
|
121
|
+
client.delete_note(note_id)
|
|
122
|
+
except NotFoundError:
|
|
123
|
+
print_error(f"Note {note_id} not found.")
|
|
124
|
+
raise typer.Exit(code=1)
|
|
125
|
+
except (AuthenticationError, APIError) as exc:
|
|
126
|
+
print_error(str(exc))
|
|
127
|
+
raise typer.Exit(code=1)
|
|
128
|
+
finally:
|
|
129
|
+
client.close()
|
|
130
|
+
|
|
131
|
+
print_success(f"Note {note_id} deleted.")
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Paper commands: add, list, view, delete."""
|
|
2
|
+
import json
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from ..auth import get_token
|
|
8
|
+
from ..client import TisitClient
|
|
9
|
+
from ..config import Config
|
|
10
|
+
from ..display import (
|
|
11
|
+
print_error, print_paper_detail, print_paper_table,
|
|
12
|
+
print_success,
|
|
13
|
+
)
|
|
14
|
+
from ..exceptions import AuthenticationError, APIError, NotFoundError
|
|
15
|
+
|
|
16
|
+
paper_app = typer.Typer(help="Manage your research papers")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _get_client() -> TisitClient:
|
|
20
|
+
token = get_token()
|
|
21
|
+
if not token:
|
|
22
|
+
print_error("Not logged in. Run: tisit login")
|
|
23
|
+
raise typer.Exit(code=1)
|
|
24
|
+
cfg = Config()
|
|
25
|
+
return TisitClient(cfg.api_url, token)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@paper_app.command("list")
|
|
29
|
+
def paper_list(
|
|
30
|
+
query: Optional[str] = typer.Option(None, "--query", "-q", help="Search title or authors"),
|
|
31
|
+
sort: str = typer.Option("uploaded_at", "--sort", "-s", help="Sort by: uploaded_at, title"),
|
|
32
|
+
order: str = typer.Option("desc", "--order", "-o", help="asc or desc"),
|
|
33
|
+
page: int = typer.Option(1, "--page", "-p"),
|
|
34
|
+
per_page: int = typer.Option(20, "--per-page"),
|
|
35
|
+
output_json: bool = typer.Option(False, "--json", help="JSON output"),
|
|
36
|
+
):
|
|
37
|
+
"""List your papers."""
|
|
38
|
+
client = _get_client()
|
|
39
|
+
try:
|
|
40
|
+
papers, meta = client.list_papers(
|
|
41
|
+
q=query, sort=sort, order=order, page=page, per_page=per_page,
|
|
42
|
+
)
|
|
43
|
+
except (AuthenticationError, APIError) as exc:
|
|
44
|
+
print_error(str(exc))
|
|
45
|
+
raise typer.Exit(code=1)
|
|
46
|
+
finally:
|
|
47
|
+
client.close()
|
|
48
|
+
|
|
49
|
+
if output_json:
|
|
50
|
+
typer.echo(json.dumps({"data": papers, "meta": meta}, indent=2, default=str))
|
|
51
|
+
else:
|
|
52
|
+
if not papers:
|
|
53
|
+
print_error("No papers found.")
|
|
54
|
+
else:
|
|
55
|
+
print_paper_table(papers, meta)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@paper_app.command("view")
|
|
59
|
+
def paper_view(
|
|
60
|
+
paper_id: int = typer.Argument(..., help="Paper ID"),
|
|
61
|
+
output_json: bool = typer.Option(False, "--json", help="JSON output"),
|
|
62
|
+
):
|
|
63
|
+
"""View a paper in detail."""
|
|
64
|
+
client = _get_client()
|
|
65
|
+
try:
|
|
66
|
+
paper = client.get_paper(paper_id)
|
|
67
|
+
except NotFoundError:
|
|
68
|
+
print_error(f"Paper {paper_id} not found.")
|
|
69
|
+
raise typer.Exit(code=1)
|
|
70
|
+
except (AuthenticationError, APIError) as exc:
|
|
71
|
+
print_error(str(exc))
|
|
72
|
+
raise typer.Exit(code=1)
|
|
73
|
+
finally:
|
|
74
|
+
client.close()
|
|
75
|
+
|
|
76
|
+
if output_json:
|
|
77
|
+
typer.echo(json.dumps(paper, indent=2, default=str))
|
|
78
|
+
else:
|
|
79
|
+
print_paper_detail(paper)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@paper_app.command("add")
|
|
83
|
+
def paper_add(
|
|
84
|
+
url: str = typer.Argument(..., help="Paper URL (PDF or article page)"),
|
|
85
|
+
title: Optional[str] = typer.Option(None, "--title", "-t", help="Paper title"),
|
|
86
|
+
authors: Optional[str] = typer.Option(None, "--authors", "-a", help="Paper authors"),
|
|
87
|
+
output_json: bool = typer.Option(False, "--json", help="JSON output"),
|
|
88
|
+
):
|
|
89
|
+
"""Add a paper by URL (async — queued for processing)."""
|
|
90
|
+
client = _get_client()
|
|
91
|
+
try:
|
|
92
|
+
data = client.add_paper(url, title=title, authors=authors)
|
|
93
|
+
except (AuthenticationError, APIError) as exc:
|
|
94
|
+
print_error(str(exc))
|
|
95
|
+
raise typer.Exit(code=1)
|
|
96
|
+
finally:
|
|
97
|
+
client.close()
|
|
98
|
+
|
|
99
|
+
if output_json:
|
|
100
|
+
typer.echo(json.dumps(data, indent=2, default=str))
|
|
101
|
+
else:
|
|
102
|
+
if data.get("is_duplicate"):
|
|
103
|
+
print_success(f"Paper already exists (ID: {data['paper_id']}). You have access.")
|
|
104
|
+
else:
|
|
105
|
+
print_success(
|
|
106
|
+
f"Paper queued for processing (ID: {data['paper_id']}).\n"
|
|
107
|
+
f" Check status with: tisit paper view {data['paper_id']}"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@paper_app.command("delete")
|
|
112
|
+
def paper_delete(
|
|
113
|
+
paper_id: int = typer.Argument(..., help="Paper ID to delete"),
|
|
114
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation"),
|
|
115
|
+
):
|
|
116
|
+
"""Delete a paper."""
|
|
117
|
+
if not yes:
|
|
118
|
+
confirm = typer.confirm(f"Delete paper {paper_id}?")
|
|
119
|
+
if not confirm:
|
|
120
|
+
raise typer.Abort()
|
|
121
|
+
|
|
122
|
+
client = _get_client()
|
|
123
|
+
try:
|
|
124
|
+
client.delete_paper(paper_id)
|
|
125
|
+
except NotFoundError:
|
|
126
|
+
print_error(f"Paper {paper_id} not found.")
|
|
127
|
+
raise typer.Exit(code=1)
|
|
128
|
+
except (AuthenticationError, APIError) as exc:
|
|
129
|
+
print_error(str(exc))
|
|
130
|
+
raise typer.Exit(code=1)
|
|
131
|
+
finally:
|
|
132
|
+
client.close()
|
|
133
|
+
|
|
134
|
+
print_success(f"Paper {paper_id} deleted.")
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Patent commands: add, list, view, delete."""
|
|
2
|
+
import json
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from ..auth import get_token
|
|
8
|
+
from ..client import TisitClient
|
|
9
|
+
from ..config import Config
|
|
10
|
+
from ..display import (
|
|
11
|
+
print_error, print_patent_detail, print_patent_table,
|
|
12
|
+
print_success,
|
|
13
|
+
)
|
|
14
|
+
from ..exceptions import AuthenticationError, APIError, NotFoundError
|
|
15
|
+
|
|
16
|
+
patent_app = typer.Typer(help="Manage your patents")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _get_client() -> TisitClient:
|
|
20
|
+
token = get_token()
|
|
21
|
+
if not token:
|
|
22
|
+
print_error("Not logged in. Run: tisit login")
|
|
23
|
+
raise typer.Exit(code=1)
|
|
24
|
+
cfg = Config()
|
|
25
|
+
return TisitClient(cfg.api_url, token)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@patent_app.command("list")
|
|
29
|
+
def patent_list(
|
|
30
|
+
query: Optional[str] = typer.Option(None, "--query", "-q", help="Search title or patent number"),
|
|
31
|
+
sort: str = typer.Option("fetched_at", "--sort", "-s"),
|
|
32
|
+
order: str = typer.Option("desc", "--order", "-o"),
|
|
33
|
+
page: int = typer.Option(1, "--page", "-p"),
|
|
34
|
+
per_page: int = typer.Option(20, "--per-page"),
|
|
35
|
+
output_json: bool = typer.Option(False, "--json", help="JSON output"),
|
|
36
|
+
):
|
|
37
|
+
"""List your patents."""
|
|
38
|
+
client = _get_client()
|
|
39
|
+
try:
|
|
40
|
+
patents, meta = client.list_patents(
|
|
41
|
+
q=query, sort=sort, order=order, page=page, per_page=per_page,
|
|
42
|
+
)
|
|
43
|
+
except (AuthenticationError, APIError) as exc:
|
|
44
|
+
print_error(str(exc))
|
|
45
|
+
raise typer.Exit(code=1)
|
|
46
|
+
finally:
|
|
47
|
+
client.close()
|
|
48
|
+
|
|
49
|
+
if output_json:
|
|
50
|
+
typer.echo(json.dumps({"data": patents, "meta": meta}, indent=2, default=str))
|
|
51
|
+
else:
|
|
52
|
+
if not patents:
|
|
53
|
+
print_error("No patents found.")
|
|
54
|
+
else:
|
|
55
|
+
print_patent_table(patents, meta)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@patent_app.command("view")
|
|
59
|
+
def patent_view(
|
|
60
|
+
patent_id: int = typer.Argument(..., help="Patent ID"),
|
|
61
|
+
output_json: bool = typer.Option(False, "--json", help="JSON output"),
|
|
62
|
+
):
|
|
63
|
+
"""View a patent in detail."""
|
|
64
|
+
client = _get_client()
|
|
65
|
+
try:
|
|
66
|
+
patent = client.get_patent(patent_id)
|
|
67
|
+
except NotFoundError:
|
|
68
|
+
print_error(f"Patent {patent_id} not found.")
|
|
69
|
+
raise typer.Exit(code=1)
|
|
70
|
+
except (AuthenticationError, APIError) as exc:
|
|
71
|
+
print_error(str(exc))
|
|
72
|
+
raise typer.Exit(code=1)
|
|
73
|
+
finally:
|
|
74
|
+
client.close()
|
|
75
|
+
|
|
76
|
+
if output_json:
|
|
77
|
+
typer.echo(json.dumps(patent, indent=2, default=str))
|
|
78
|
+
else:
|
|
79
|
+
print_patent_detail(patent)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@patent_app.command("add")
|
|
83
|
+
def patent_add(
|
|
84
|
+
url_or_number: str = typer.Argument(..., help="Patent URL or patent number"),
|
|
85
|
+
output_json: bool = typer.Option(False, "--json", help="JSON output"),
|
|
86
|
+
):
|
|
87
|
+
"""Add a patent by URL or number (async — queued for processing)."""
|
|
88
|
+
# Detect whether input is a URL or patent number
|
|
89
|
+
is_url = url_or_number.startswith("http")
|
|
90
|
+
|
|
91
|
+
client = _get_client()
|
|
92
|
+
try:
|
|
93
|
+
if is_url:
|
|
94
|
+
data = client.add_patent(url=url_or_number)
|
|
95
|
+
else:
|
|
96
|
+
data = client.add_patent(patent_number=url_or_number)
|
|
97
|
+
except (AuthenticationError, APIError) as exc:
|
|
98
|
+
print_error(str(exc))
|
|
99
|
+
raise typer.Exit(code=1)
|
|
100
|
+
finally:
|
|
101
|
+
client.close()
|
|
102
|
+
|
|
103
|
+
if output_json:
|
|
104
|
+
typer.echo(json.dumps(data, indent=2, default=str))
|
|
105
|
+
else:
|
|
106
|
+
if data.get("is_duplicate"):
|
|
107
|
+
print_success(f"Patent already exists (ID: {data['patent_id']}).")
|
|
108
|
+
else:
|
|
109
|
+
print_success(
|
|
110
|
+
f"Patent queued for processing (ID: {data['patent_id']}).\n"
|
|
111
|
+
f" Check status with: tisit patent view {data['patent_id']}"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@patent_app.command("delete")
|
|
116
|
+
def patent_delete(
|
|
117
|
+
patent_id: int = typer.Argument(..., help="Patent ID to delete"),
|
|
118
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation"),
|
|
119
|
+
):
|
|
120
|
+
"""Delete a patent."""
|
|
121
|
+
if not yes:
|
|
122
|
+
confirm = typer.confirm(f"Delete patent {patent_id}?")
|
|
123
|
+
if not confirm:
|
|
124
|
+
raise typer.Abort()
|
|
125
|
+
|
|
126
|
+
client = _get_client()
|
|
127
|
+
try:
|
|
128
|
+
client.delete_patent(patent_id)
|
|
129
|
+
except NotFoundError:
|
|
130
|
+
print_error(f"Patent {patent_id} not found.")
|
|
131
|
+
raise typer.Exit(code=1)
|
|
132
|
+
except (AuthenticationError, APIError) as exc:
|
|
133
|
+
print_error(str(exc))
|
|
134
|
+
raise typer.Exit(code=1)
|
|
135
|
+
finally:
|
|
136
|
+
client.close()
|
|
137
|
+
|
|
138
|
+
print_success(f"Patent {patent_id} deleted.")
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""Podcast commands: add, list, view, delete."""
|
|
2
|
+
import json
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from ..auth import get_token
|
|
8
|
+
from ..client import TisitClient
|
|
9
|
+
from ..config import Config
|
|
10
|
+
from ..display import (
|
|
11
|
+
print_error, print_podcast_detail, print_podcast_table,
|
|
12
|
+
print_success,
|
|
13
|
+
)
|
|
14
|
+
from ..exceptions import AuthenticationError, APIError, NotFoundError
|
|
15
|
+
|
|
16
|
+
podcast_app = typer.Typer(help="Manage your podcast episodes")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _get_client() -> TisitClient:
|
|
20
|
+
token = get_token()
|
|
21
|
+
if not token:
|
|
22
|
+
print_error("Not logged in. Run: tisit login")
|
|
23
|
+
raise typer.Exit(code=1)
|
|
24
|
+
cfg = Config()
|
|
25
|
+
return TisitClient(cfg.api_url, token)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@podcast_app.command("list")
|
|
29
|
+
def podcast_list(
|
|
30
|
+
query: Optional[str] = typer.Option(None, "--query", "-q", help="Search title or podcast name"),
|
|
31
|
+
sort: str = typer.Option("created_at", "--sort", "-s"),
|
|
32
|
+
order: str = typer.Option("desc", "--order", "-o"),
|
|
33
|
+
page: int = typer.Option(1, "--page", "-p"),
|
|
34
|
+
per_page: int = typer.Option(20, "--per-page"),
|
|
35
|
+
output_json: bool = typer.Option(False, "--json", help="JSON output"),
|
|
36
|
+
):
|
|
37
|
+
"""List your podcast episodes."""
|
|
38
|
+
client = _get_client()
|
|
39
|
+
try:
|
|
40
|
+
podcasts, meta = client.list_podcasts(
|
|
41
|
+
q=query, sort=sort, order=order, page=page, per_page=per_page,
|
|
42
|
+
)
|
|
43
|
+
except (AuthenticationError, APIError) as exc:
|
|
44
|
+
print_error(str(exc))
|
|
45
|
+
raise typer.Exit(code=1)
|
|
46
|
+
finally:
|
|
47
|
+
client.close()
|
|
48
|
+
|
|
49
|
+
if output_json:
|
|
50
|
+
typer.echo(json.dumps({"data": podcasts, "meta": meta}, indent=2, default=str))
|
|
51
|
+
else:
|
|
52
|
+
if not podcasts:
|
|
53
|
+
print_error("No podcast episodes found.")
|
|
54
|
+
else:
|
|
55
|
+
print_podcast_table(podcasts, meta)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@podcast_app.command("view")
|
|
59
|
+
def podcast_view(
|
|
60
|
+
podcast_id: int = typer.Argument(..., help="Podcast episode ID"),
|
|
61
|
+
output_json: bool = typer.Option(False, "--json", help="JSON output"),
|
|
62
|
+
):
|
|
63
|
+
"""View a podcast episode in detail."""
|
|
64
|
+
client = _get_client()
|
|
65
|
+
try:
|
|
66
|
+
podcast = client.get_podcast(podcast_id)
|
|
67
|
+
except NotFoundError:
|
|
68
|
+
print_error(f"Podcast episode {podcast_id} not found.")
|
|
69
|
+
raise typer.Exit(code=1)
|
|
70
|
+
except (AuthenticationError, APIError) as exc:
|
|
71
|
+
print_error(str(exc))
|
|
72
|
+
raise typer.Exit(code=1)
|
|
73
|
+
finally:
|
|
74
|
+
client.close()
|
|
75
|
+
|
|
76
|
+
if output_json:
|
|
77
|
+
typer.echo(json.dumps(podcast, indent=2, default=str))
|
|
78
|
+
else:
|
|
79
|
+
print_podcast_detail(podcast)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@podcast_app.command("add")
|
|
83
|
+
def podcast_add(
|
|
84
|
+
url: str = typer.Argument(..., help="Podcast episode URL (Spotify, Apple, RSS)"),
|
|
85
|
+
output_json: bool = typer.Option(False, "--json", help="JSON output"),
|
|
86
|
+
):
|
|
87
|
+
"""Add a podcast episode by URL (async — queued for processing)."""
|
|
88
|
+
client = _get_client()
|
|
89
|
+
try:
|
|
90
|
+
data = client.add_podcast(url)
|
|
91
|
+
except (AuthenticationError, APIError) as exc:
|
|
92
|
+
print_error(str(exc))
|
|
93
|
+
raise typer.Exit(code=1)
|
|
94
|
+
finally:
|
|
95
|
+
client.close()
|
|
96
|
+
|
|
97
|
+
if output_json:
|
|
98
|
+
typer.echo(json.dumps(data, indent=2, default=str))
|
|
99
|
+
else:
|
|
100
|
+
if data.get("is_duplicate"):
|
|
101
|
+
print_success(f"Podcast episode already exists (ID: {data['podcast_id']}).")
|
|
102
|
+
else:
|
|
103
|
+
print_success(
|
|
104
|
+
f"Podcast episode queued for processing (ID: {data['podcast_id']}).\n"
|
|
105
|
+
f" Check status with: tisit podcast view {data['podcast_id']}"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@podcast_app.command("delete")
|
|
110
|
+
def podcast_delete(
|
|
111
|
+
podcast_id: int = typer.Argument(..., help="Podcast episode ID to delete"),
|
|
112
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation"),
|
|
113
|
+
):
|
|
114
|
+
"""Delete a podcast episode."""
|
|
115
|
+
if not yes:
|
|
116
|
+
confirm = typer.confirm(f"Delete podcast episode {podcast_id}?")
|
|
117
|
+
if not confirm:
|
|
118
|
+
raise typer.Abort()
|
|
119
|
+
|
|
120
|
+
client = _get_client()
|
|
121
|
+
try:
|
|
122
|
+
client.delete_podcast(podcast_id)
|
|
123
|
+
except NotFoundError:
|
|
124
|
+
print_error(f"Podcast episode {podcast_id} not found.")
|
|
125
|
+
raise typer.Exit(code=1)
|
|
126
|
+
except (AuthenticationError, APIError) as exc:
|
|
127
|
+
print_error(str(exc))
|
|
128
|
+
raise typer.Exit(code=1)
|
|
129
|
+
finally:
|
|
130
|
+
client.close()
|
|
131
|
+
|
|
132
|
+
print_success(f"Podcast episode {podcast_id} deleted.")
|