* fix(stream-gears): raise exceptions instead of failing silently in the Python API
- Add `stream_gears.StreamGearsError` (subclass of `RuntimeError`, so
existing `except RuntimeError` keeps working). Errors that used to raise
`RuntimeError` now raise it, with the full error-stack cause chain.
- `download` / `download_with_callback`: connection failures, an
incomplete first packet, FLV errors mid-stream and HLS errors raise
`StreamGearsError` instead of panicking (`PanicException` derives from
`BaseException`) or only logging and returning. The FLV path calls
`httpflv::parse_flv`, now public, so `httpflv::download` and server-side
recording are unchanged.
- `download_with_callback` accepts any callable. A callback exception is
logged when it happens and re-raised once the download finishes; if the
download fails too, it becomes the `__cause__` of the `StreamGearsError`.
- `PySegment` fields are readable; `segment` also accepts a dict or an
object with `time` / `size` attributes, and `desc_v2` items accept
objects with `type_id` / `raw_text` / `biz_id` besides dicts.
- `upload`: malformed `extra_fields` raises `ValueError` instead of being
dropped; empty or blank still means none.
- `login_by_sms` raises on failure instead of returning `False`; invalid
`ret` JSON raises `ValueError`; tokio runtime creation errors raise
instead of panicking.
- The seven login functions get `proxy=None` defaults (PyO3 >= 0.24 no
longer defaults trailing `Option` arguments).
Ref #1721
* test(stream-gears): offline pytest cases for error semantics and argument types
Ref #1721
* fix(stream-gears): turn FLV parser panics in download into StreamGearsError
`httpflv::parse_flv` still `expect`s on malformed AAC/AVC/script tag data.
Catch the unwind at the binding boundary so `download` raises
`StreamGearsError` instead of `PanicException`, without changing the
parser that server-side recording shares.
Ref #1721
* test(stream-gears): update smoke suite for StreamGearsError and optional proxy
The login functions now default proxy to None, so the strict xfail cases
for it pass and are removed (test_errors.py covers the same assertion).
StreamGearsError joins the exported names; PySegment fields are readable.
* feat(stream-gears): optional cookie file path for login functions
`login_by_sms`, `login_by_web_cookies` and `login_by_web_qrcode` take a
`file` argument (default `"cookies.json"`, same as before) for where the
login info is written. `login_by_qrcode` takes `file=None` and writes the
login info there when given; it still returns the JSON string.
Ref #1721