simklcli 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.
@@ -0,0 +1,217 @@
1
+ Metadata-Version: 2.4
2
+ Name: simklcli
3
+ Version: 0.1.0
4
+ Summary: Track movies, shows, and anime on Simkl from the command line.
5
+ Author: Javier Ochoa
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Javier Ochoa
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ License-File: LICENSE
28
+ Requires-Python: >=3.11
29
+ Requires-Dist: filelock<4,>=3.18
30
+ Requires-Dist: httpx<1,>=0.28
31
+ Requires-Dist: keyring<26,>=25
32
+ Requires-Dist: platformdirs<5,>=4.3
33
+ Requires-Dist: rich<15,>=14
34
+ Requires-Dist: typer<1,>=0.27
35
+ Description-Content-Type: text/markdown
36
+
37
+ # simklcli
38
+
39
+ `simklcli` is a public command-line client for the core Simkl tracking loop:
40
+ authentication, catalog search and lookup, Library management, Watched State,
41
+ and User Ratings. The PyPI distribution exposes the shorter `simkl` command.
42
+
43
+ The CLI supports Simkl PIN Authorization, catalog search and lookup, Library
44
+ management, Watched State, and User Ratings. It reconciles account state online
45
+ and retains a dated Library Snapshot for explicit offline reads.
46
+
47
+ ## Install
48
+
49
+ Python 3.11 or newer is required. Once the first release is published, install
50
+ the supported package with:
51
+
52
+ ```sh
53
+ uv tool install simklcli
54
+ # or: pipx install simklcli
55
+ ```
56
+
57
+ ## Authentication
58
+
59
+ The CLI ships with simklcli's public AUTH V2 app ID, so no developer
60
+ registration is needed:
61
+
62
+ ```sh
63
+ simkl auth login
64
+ simkl auth status --check
65
+ simkl search "Cowboy Bebop" --kind anime
66
+ simkl library list
67
+ simkl library list --offline
68
+ ```
69
+
70
+ From a checkout, run the same commands through `uv sync --locked` and
71
+ `uv run simkl …`.
72
+
73
+ Login uses the V2 device-based PIN Authorization flow. Open the printed link
74
+ and approve the application on Simkl; no client secret, redirect URL, or PKCE
75
+ parameters are used by this flow. The client ID is public by design: on its
76
+ own it reaches only public catalog data, and every user-scoped request also
77
+ needs that user's Access Token.
78
+
79
+ To use your own app instead, register an **AUTH V2** application as
80
+ **TV, devices & command line** at
81
+ [Simkl Developer Settings](https://simkl.com/settings/developer/) and set
82
+ `SIMKL_CLIENT_ID` to its public ID before logging in. The issuing app ID is
83
+ stored with each credential, so later invocations keep using it without the
84
+ environment variable. An override must match the app that issued the stored
85
+ tokens.
86
+
87
+ ```sh
88
+ simkl auth login
89
+ simkl auth status
90
+ simkl auth status --check
91
+ simkl auth logout
92
+ ```
93
+
94
+ Login stores the Access Token and Refresh Token together in the OS keyring.
95
+ If the platform has no usable keyring, plaintext storage must be
96
+ selected explicitly with `simkl auth login --storage file`. Set
97
+ `SIMKL_ACCESS_TOKEN` for a non-persisted automation override; it takes
98
+ precedence over local credentials and must come from the built-in app or from
99
+ the app named by `SIMKL_CLIENT_ID`.
100
+ Environment overrides are not refreshed or persisted; supply a fresh Access
101
+ Token when it expires. The old embedded V1 client ID has been removed.
102
+
103
+ Access Tokens last seven days; the CLI refreshes shortly before expiry or once
104
+ after an explicit token rejection. Refresh Tokens last 180 days with sliding
105
+ expiry. Refresh and storage changes share an account lock across processes.
106
+ Search and External ID resolution require sign-in. An anonymous known-ID lookup
107
+ is available with `simkl lookup simkl:<id> --kind movie|show|anime`.
108
+
109
+ An existing V1 account reconnects through `simkl auth login` without first
110
+ logging out: approval must match the recorded account, and its Library Snapshot
111
+ is retained. A failed or cancelled login preserves the existing credentials.
112
+ Expired or revoked grants require explicit login and keep offline snapshots.
113
+ See the [migration guide](docs/auth-v2-migration.md) for local verification.
114
+
115
+ Logout removes local credentials and the Library Snapshot and makes a
116
+ best-effort request to revoke the stored V2 grant. `--local-only` skips that
117
+ request. Simkl acknowledges revocation without revealing whether a token was
118
+ valid; the CLI reports acknowledgement rather than proof of revocation.
119
+
120
+ Use `simkl auth status --json` for a single machine-readable stdout payload.
121
+ The default status check is local-only; `--check` explicitly validates the
122
+ Access Token with Simkl and refreshes the display name.
123
+
124
+ ## Development
125
+
126
+ ```sh
127
+ uv sync --locked
128
+ uv run ruff check .
129
+ uv run mypy src tests scripts
130
+ uv run pytest
131
+ uv build --no-sources
132
+ ```
133
+
134
+ ## Tracking
135
+
136
+ ```sh
137
+ simkl search "Cowboy Bebop" --kind anime
138
+ simkl lookup imdb:tt0133093
139
+ simkl lookup tmdb:603 --kind movie
140
+ simkl library list --kind anime --status watching
141
+ simkl library set-status simkl:3708 plan-to-watch
142
+ simkl watched mark simkl:3708 --episode 1 --at 2026-08-08T20:00:00Z
143
+ simkl watched mark simkl:3708 --all --yes
144
+ simkl watched unmark simkl:3708 --episode 1
145
+ simkl rating set simkl:3708 9
146
+ simkl rating remove simkl:3708
147
+ simkl library remove simkl:3708 --yes
148
+ simkl library list --offline --json
149
+ simkl library repair
150
+ ```
151
+
152
+ Media References accept `simkl:<id>`, supported provider-qualified External
153
+ IDs, or title text with optional `--kind` and `--year`. TMDB IDs require
154
+ `--kind movie` or `--kind show`. An ambiguous title returns candidates and fails
155
+ in scripts; interactive terminals can select a candidate. Search accepts
156
+ `--page` (1–20) and `--limit` (1–50 results per media kind).
157
+
158
+ Movies and standalone Anime take no Episode selectors. Shows require
159
+ `--season N --episode N`; episodic Anime uses `--episode N` without a Season.
160
+ Both accept `--all`; Shows also accept `--season N --all`. Bulk marks select
161
+ aired Episodes. `--episode-id tvdb:<id>` or `--episode-id anidb:<id>` accepts an
162
+ episode-level External ID with a parent Media Reference. When a response to
163
+ such a write is lost, parent state alone cannot prove which Episode changed,
164
+ so the CLI reports an unknown outcome instead of encouraging a blind retry.
165
+
166
+ `watched mark --rewatch` explicitly records another Viewing and requires
167
+ Simkl PRO or VIP. Use `--rewatch-id` to resume a specific known session.
168
+ Unmark Watched preserves Library membership, List Status, and User Rating;
169
+ `library remove` deletes all three along with Watched State.
170
+ Simkl makes Completed Movies watched automatically, so `watched unmark` rejects
171
+ that case before changing anything. Use an explicit `library set-status` move
172
+ to `plan-to-watch` or `dropped` to clear a Movie's Watched State while keeping
173
+ its User Rating.
174
+
175
+ Every command accepts `--json`: stdout contains one JSON result, diagnostics
176
+ go to stderr, and prompts are disabled. Whole-item removal, bulk updates, and
177
+ logout require `--yes` in JSON or noninteractive use. User Ratings range from
178
+ 1 to 10 and apply to Movies, Shows, and Anime.
179
+
180
+ Environment-token accounts can read their snapshots offline after one online
181
+ Library read validates their identity. Only a token fingerprint and account ID
182
+ are retained for that association; the Access Token is never persisted.
183
+
184
+ Online Library reads check activities, merge changed List Status and rating
185
+ state, and compare IDs when removals occur. Offline reads require a complete
186
+ snapshot and report its age. Focused writes work before that first complete
187
+ read. A successful remote write followed by failed reconciliation reports
188
+ success and dirty local state; the next online read reconciles it. No writes
189
+ are queued or automatically replayed.
190
+
191
+ ## Releases
192
+
193
+ The [release guide](docs/releasing.md) describes the mandatory live smoke,
194
+ restoration backup, GitHub environments, PyPI Trusted Publishing, and version
195
+ tag checks. Merge-gating tests use authored HTTPX responses and never contact
196
+ Simkl; the manually triggered smoke is the only live test workflow.
197
+
198
+ ## Project map
199
+
200
+ - [`docs/spec.md`](docs/spec.md) — build-ready v1 product and engineering specification
201
+ - [`CONTEXT.md`](CONTEXT.md) — canonical domain vocabulary
202
+ - [`docs/adr/`](docs/adr/) — architectural decisions
203
+ - [`docs/research/`](docs/research/) — cited API and prior-art research
204
+ - [`src/simklcli/`](src/simklcli/) — installable tracking CLI
205
+ - [`tests/`](tests/) — offline unit, HTTPX transport, and Typer CLI tests
206
+ - [`prototypes/command-surface/`](prototypes/command-surface/) — runnable Typer command-surface prototype
207
+
208
+ Run the prototype from the repository root:
209
+
210
+ ```sh
211
+ ./prototypes/command-surface/run surface
212
+ ./prototypes/command-surface/run --help
213
+ ```
214
+
215
+ ## License
216
+
217
+ MIT. See [`LICENSE`](LICENSE).
@@ -0,0 +1,20 @@
1
+ simklcli/__init__.py,sha256=6dywuJOhdI4NoXIu6i_yUqiPjZiyRLiWdIgBNFOlTkE,47
2
+ simklcli/__main__.py,sha256=9COydJZB76bwe42rh0BUe6U4DbBdgzI1-mq9DqxDR4s,67
3
+ simklcli/api.py,sha256=V2rxL-ApGAFmDwf5_wU6qKCIVji5dRMi7VT7six2Fc0,16052
4
+ simklcli/catalog.py,sha256=iQyz3h3vvlTk_6l_h4giLQVI3tRZj9Zle9UlCMPzPpE,6926
5
+ simklcli/cli.py,sha256=o4gP61MTI1Hh7ScGJym129XoCxqeCM27Tz09CnVhTlw,20375
6
+ simklcli/commands.py,sha256=4MMgc1IDqDN_rl0HkFyOi5rSq_s6DCLAPh0wk1U_Kjc,17203
7
+ simklcli/filesystem.py,sha256=DTDTERLHK-u1051hrOJPerKauX0BpbbCkmL56_f_nho,925
8
+ simklcli/media.py,sha256=X3dnJPtG1B5RFVckfcdyjze2lZTbyW15yU2pKa6SAUc,6650
9
+ simklcli/output.py,sha256=wow9jcoaws4sRaeSnqeMi2bsBbSrcEFAu4lyfCB6QHQ,1818
10
+ simklcli/rate_control.py,sha256=UzIjZ_ADzw1BnX4CwlhHzJ7pvhjgd-B82_wfG9La1wo,3831
11
+ simklcli/runtime.py,sha256=aOWe0FCzNXoGJoRnxrXUcbdixqr9vNL1yQsLngZ6-Q0,9668
12
+ simklcli/snapshots.py,sha256=_A0wNpXJ7Lw54SgFJmmCY1pvxs3PG89GzVOly4XnLNA,6891
13
+ simklcli/storage.py,sha256=0I8VoFznfqXsS0OsxvsxEzaFWswD40AfxP422Kecup4,13685
14
+ simklcli/targets.py,sha256=Zj5fhZqJ9_cLSid13hWM6V-wRjekZc4e13n1iLWOYyI,5163
15
+ simklcli/tracking.py,sha256=adIAuV7-NmvcfgdQJkVwBS7Dl18oSS7Eqo5oZct-Etc,20913
16
+ simklcli-0.1.0.dist-info/METADATA,sha256=iODURbeL1co1vs_YVcOTfJ2reLHMZK8SkmqI0q5orpQ,9389
17
+ simklcli-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
+ simklcli-0.1.0.dist-info/entry_points.txt,sha256=83TaZ6LnJDx60INKPBTGj43KmVFMWSbD2HN6_2ca4GM,43
19
+ simklcli-0.1.0.dist-info/licenses/LICENSE,sha256=n_CWabjY_86040ujq4X0r5VBTGNM9KriO2cK5JvdmnI,1069
20
+ simklcli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ simkl = simklcli.cli:app
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Javier Ochoa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.