Metadata-Version: 2.4
Name: nodeseek-cli
Version: 0.1.0
Summary: Command-line reader for NodeSeek forum threads that outputs Markdown or JSON
Project-URL: Homepage, https://github.com/rumisle/nodeseek-cli
Project-URL: Repository, https://github.com/rumisle/nodeseek-cli
Project-URL: Issues, https://github.com/rumisle/nodeseek-cli/issues
Author: nodeseek-cli contributors
License: MIT
License-File: LICENSE
Keywords: cli,forum,json,markdown,nodeseek,scraper
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Text Processing :: Markup :: Markdown
Requires-Python: >=3.10
Requires-Dist: beautifulsoup4>=4.11.0
Requires-Dist: lxml>=4.9.0
Requires-Dist: requests>=2.28.0
Provides-Extra: dev
Requires-Dist: prek>=0.1.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.6.0; extra == 'dev'
Description-Content-Type: text/markdown

# nodeseek-cli

A command-line reader for [NodeSeek](https://www.nodeseek.com) forum threads.

NodeSeek serves each thread as fully server-rendered HTML, so `nodeseek-cli` fetches a post URL
(no API key, no login) and converts it to Markdown, or to structured JSON with `--json`. It handles
pagination, quoted replies, images, code blocks, tables, and NodeSeek's "magic tab" benchmark
widgets.

## Install / run

Run without installing (recommended):

```bash
uvx nodeseek-cli 355740
```

Or install:

```bash
uv tool install nodeseek-cli      # global CLI: `nodeseek` and `nodeseek-cli`
pip install nodeseek-cli          # into current environment
```

## Usage

```
nodeseek <post> [options]

  <post>            bare id (355740), slug (post-355740-1), or full URL

  -p, --page N      fetch only page N (overrides page in the URL)
  -a, --all         fetch every page of the thread and merge
  -j, --json        emit structured JSON instead of markdown
      --ansi        keep ANSI colour codes inside code blocks (default: strip)
  -V, --version     print version
  -h, --help        show help
```

By default only **one page** is fetched (the page in the URL/slug, or page 1). The markdown
header shows `page X/N` and a footer tells you how to reach the next page or the whole thread.

### Examples

```bash
nodeseek 355740                                   # first page as markdown
nodeseek post-250906-2                             # a specific page
nodeseek 250906 --all                             # whole thread, merged
nodeseek 250906 --all --json | jq '.comments[].author'
nodeseek https://www.nodeseek.com/post-355740-1 --ansi
```

## Formatting notes

- **Stickers** are meaningless out of context and render as `[Sticker]`.
- **Images** render as markdown `![alt](url)` with absolute URLs.
- **Replies** are detected from the leading `@user #N` marker and shown as a `Reply to @user #N`
  line (and a `reply_to` object in JSON); the marker is stripped from the body.
- **Magic tab** widgets (hardware/network benchmark reports, etc.) expand into one titled code
  block per tab. ANSI colour is reconstructed from NodeSeek's `data-ansicode` encoding and
  stripped by default (keep it with `--ansi`).

## Library API

```python
from nodeseek_cli import fetch_post, to_markdown, to_json

post = fetch_post("250906", all_pages=True)   # -> nodeseek_cli.models.Post
print(post.title, post.pages, len(post.comments))

for c in post.comments:
    print(c.floor, c.author, c.reply_to)

print(to_markdown(post))
print(to_json(post))
```

Parse already-downloaded HTML offline:

```python
from nodeseek_cli import parse_page

title, max_page, comments = parse_page(html, ansi=False)
```

### Errors

`fetch_post` / `fetch_page` raise `PostNotFound` (404), `RateLimited` (429), or the base
`NodeSeekError`. `parse_target` raises `ValueError` on an unparseable reference.

## Development

```bash
uv sync --extra dev
uv run pytest            # offline tests against saved fixtures
uv run nodeseek 355740   # run the CLI from source
```

Linting and formatting use [ruff](https://docs.astral.sh/ruff/), wired up through
[prek](https://github.com/j178/prek) (a drop-in pre-commit runner):

```bash
uv run ruff format .     # format
uv run ruff check .      # lint
uv run prek install      # install the git pre-commit hook
uv run prek run --all-files
```

## License

MIT
