Skip to content

新增cli命令 jmv 用于快速获取禁漫车号对应的本子详情#521

Merged
hect0x7 merged 4 commits intomasterfrom
dev
Mar 22, 2026
Merged

新增cli命令 jmv 用于快速获取禁漫车号对应的本子详情#521
hect0x7 merged 4 commits intomasterfrom
dev

Conversation

@hect0x7
Copy link
Copy Markdown
Owner

@hect0x7 hect0x7 commented Mar 22, 2026

Summary by CodeRabbit

  • New Features

    • Added a new jmv command to view album details without downloading; accepts direct IDs or mixed text (extracts digits) and shows title, JM, link, authors/tags (truncated), dates, stats and chapter list.
  • Documentation

    • Added jmv usage, examples, sample console output and option-file configuration to README and tutorial.
  • Tests

    • Added CLI tests for ID extraction, truncation behavior and printed album-detail output.
  • Chores

    • Bumped package version.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 22, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 07b7d094-9ca7-4df4-9bba-d581fbbb7c20

📥 Commits

Reviewing files that changed from the base of the PR and between dc4c139 and e0a2029.

📒 Files selected for processing (3)
  • README.md
  • assets/docs/sources/tutorial/2_command_line.md
  • src/jmcomic/cl.py
✅ Files skipped from review due to trivial changes (1)
  • assets/docs/sources/tutorial/2_command_line.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • README.md

📝 Walkthrough

Walkthrough

Adds a new CLI command jmv for viewing album details (no download), updates README and tutorial docs, registers the jmv console script in setup, implements JmViewUI and view_main() for parsing and extracting album IDs from arbitrary text, prints formatted/truncated album details, and adds CLI tests.

Changes

Cohort / File(s) Summary
Documentation
README.md, assets/docs/sources/tutorial/2_command_line.md
Added jmv docs and examples, described album-ID extraction from mixed text (optional JM prefix), --option / JM_OPTION_PATH usage, sample output, and truncation behavior for long lists.
Entry Point
setup.py
Registered new console script jmv = jmcomic.cl:view_main alongside existing jmcomic.
CLI Implementation
src/jmcomic/cl.py
Added JmViewUI and view_main(): parse positional text and optional --option, normalize option path, extract album ID by concatenating digit groups (error -> exit 1), create client from options, call get_album_detail(album_id), format/truncate lists, print album and episode info, and optionally pause unless -y/--yes.
Tests
tests/test_jmcomic/test_jm_cli.py
Added tests for CLI parsing and JmViewUI: album/photo ID extraction, JM prefix handling, failure on missing digits (SystemExit code 1), _truncate_list() behavior, plus integration-style tests that call real API and assert printed output.
Minor Formatting
src/jmcomic/jm_toolkit.py
Inserted a blank line between JmcomicText.compare_versions() and a DSL registration block (no functional change).
Version Bump
src/jmcomic/__init__.py
Incremented __version__ from 2.6.15 to 2.6.16.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant CLI as JmViewUI
    participant Arg as ArgParser
    participant Client as APIClient
    participant API as AlbumDetailAPI

    User->>CLI: run `jmv` with text and optional --option
    CLI->>Arg: parse arguments (text, option)
    Arg-->>CLI: parsed args
    CLI->>CLI: extract_album_id (concat digit groups)
    alt no digits
        CLI->>User: log error & exit(1)
    else digits found
        CLI->>Client: create client (from option/JM_OPTION_PATH)
        CLI->>Client: get_album_detail(album_id)
        Client->>API: request album details
        API-->>Client: album data / error
        alt success
            Client-->>CLI: album object
            CLI->>CLI: format/truncate fields
            CLI-->>User: print album and episodes
        else failure
            Client-->>CLI: exception
            CLI->>User: log error & exit(1)
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I nibbled digits from muddled text,
jmv showed titles with nary a fetch,
A hop to peek, no download spree,
Chapters and tags served quick for me,
Tiny paws, swift glance, joyously free.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically describes the main change: adding a new jmv CLI command for quickly retrieving album details from a car number.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
src/jmcomic/cl.py (2)

129-160: Initialize option_path in __init__ to avoid attribute-outside-init.

The # noinspection PyAttributeOutsideInit comment indicates awareness of the issue, but initializing option_path in __init__ would be cleaner and consistent with how raw_text is handled.

📝 Proposed fix
 class JmViewUI:

     def __init__(self) -> None:
         self.raw_text: str = ''
+        self.option_path: Optional[str] = None

-    # noinspection PyAttributeOutsideInit
     def parse_arg(self):

Also add Optional import if not present (it's already imported at line 16).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/cl.py` around lines 129 - 160, The class JmViewUI sets
self.option_path in parse_arg which triggers attribute-outside-init warnings;
initialize self.option_path = None in JmViewUI.__init__ alongside self.raw_text
and ensure typing.Optional is imported (or already present) so the attribute can
be annotated as Optional[str]; update __init__ and keep parse_arg logic
unchanged (references: class JmViewUI, method __init__, method parse_arg,
attribute option_path).

228-232: Consider catching a more specific exception.

Catching a bare Exception is flagged by Ruff (BLE001). While acceptable for CLI error handling, catching a more specific exception type would be cleaner if the client API has defined exception types.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/cl.py` around lines 228 - 232, The try/except around
client.get_album_detail(album_id) is currently catching a bare Exception; change
it to catch the client library's specific error type(s) (for example
ClientError, APIError, or the specific exception class the client exposes)
instead of Exception, e.g., except ClientError as e:, so you still call
jm_log('jmv', f'❌❌❌ 获取失败: album {album_id} 详情请求出错, 原因: {e}', e) and exit(1) on
failure; if the client has multiple error classes catch a tuple of them, and
only fall back to catching Exception if no specific types exist.
README.md (1)

170-183: Add language identifier to fenced code block.

The output example code block lacks a language specification, which triggers the markdownlint MD040 warning. Since this is console output, consider using text or console as the language identifier.

📝 Proposed fix
 输出效果:
-```
+```text
 ──────────────────────────────────────────────────
   📖 标题:  xxx
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 170 - 183, The fenced code block in README.md is
missing a language identifier which triggers markdownlint MD040; update the
opening fence from ``` to ```text (or ```console) for the example block shown
(the ASCII output block starting with
"──────────────────────────────────────────────────") so the block is treated as
plain text/console output and the linter warning is resolved.
assets/docs/sources/tutorial/2_command_line.md (1)

57-81: Add language identifier to output example code block.

The output example code block at line 57 lacks a language specification. Consider using text for consistency with typical documentation standards.

📝 Proposed fix
 ### 2.3 输出示例

-```
+```text
 🔍 正在查询 禁漫车号 - [350234] 的详情...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/docs/sources/tutorial/2_command_line.md` around lines 57 - 81, The
fenced example output block starting with "🔍 正在查询 禁漫车号 - [350234] 的详情..." is
missing a language identifier; update that code fence to include the language
tag "text" (i.e., change the opening ``` to ```text) so the block is rendered
consistently, and apply the same change to any other similar output blocks in
the document.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@assets/docs/sources/tutorial/2_command_line.md`:
- Around line 57-81: The fenced example output block starting with "🔍 正在查询 禁漫车号
- [350234] 的详情..." is missing a language identifier; update that code fence to
include the language tag "text" (i.e., change the opening ``` to ```text) so the
block is rendered consistently, and apply the same change to any other similar
output blocks in the document.

In `@README.md`:
- Around line 170-183: The fenced code block in README.md is missing a language
identifier which triggers markdownlint MD040; update the opening fence from ```
to ```text (or ```console) for the example block shown (the ASCII output block
starting with "──────────────────────────────────────────────────") so the block
is treated as plain text/console output and the linter warning is resolved.

In `@src/jmcomic/cl.py`:
- Around line 129-160: The class JmViewUI sets self.option_path in parse_arg
which triggers attribute-outside-init warnings; initialize self.option_path =
None in JmViewUI.__init__ alongside self.raw_text and ensure typing.Optional is
imported (or already present) so the attribute can be annotated as
Optional[str]; update __init__ and keep parse_arg logic unchanged (references:
class JmViewUI, method __init__, method parse_arg, attribute option_path).
- Around line 228-232: The try/except around client.get_album_detail(album_id)
is currently catching a bare Exception; change it to catch the client library's
specific error type(s) (for example ClientError, APIError, or the specific
exception class the client exposes) instead of Exception, e.g., except
ClientError as e:, so you still call jm_log('jmv', f'❌❌❌ 获取失败: album {album_id}
详情请求出错, 原因: {e}', e) and exit(1) on failure; if the client has multiple error
classes catch a tuple of them, and only fall back to catching Exception if no
specific types exist.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5d22d475-e238-407f-b828-f6e5057b3e49

📥 Commits

Reviewing files that changed from the base of the PR and between 39b1a2e and 16e5a00.

📒 Files selected for processing (4)
  • README.md
  • assets/docs/sources/tutorial/2_command_line.md
  • setup.py
  • src/jmcomic/cl.py

@hect0x7 hect0x7 changed the title 新增cl命令 jmv 用于快速获取禁漫车号对应的本子详情 新增cli命令 jmv 用于快速获取禁漫车号对应的本子详情 Mar 22, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/jmcomic/cl.py (1)

131-159: Consider extracting common option path parsing logic.

The option path handling in parse_arg() (lines 155-159) duplicates logic from JmcomicUI.parse_arg() (lines 55-59). A small helper function could reduce this repetition:

def _resolve_option_path(option_str: str) -> Optional[str]:
    if len(option_str) == 0 or option_str == "''":
        return None
    return os.path.abspath(option_str)

This is a minor duplication and can be addressed later if more CLI commands are added.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/cl.py` around lines 131 - 159, The option path normalization in
Jmcomic.parse_arg duplicates logic from JmcomicUI.parse_arg; extract that into a
shared helper (e.g., _resolve_option_path(option_str: str) -> Optional[str])
that returns None when option_str is empty or "''" and otherwise returns
os.path.abspath(option_str), then replace the duplicate block in parse_arg (and
update JmcomicUI.parse_arg to call the same helper) so both classes reuse the
single function for resolving option paths.
tests/test_jmcomic/test_jm_cli.py (1)

96-134: Consider CI resilience for network-dependent tests.

These tests depend on real network requests to an external service. While integration tests are valuable, they may cause CI flakiness if:

  • The service is temporarily unavailable
  • Album 350234 is modified or removed
  • Network conditions are unstable

Consider adding a skip decorator or environment check for CI environments where these tests should be optional:

`@unittest.skipIf`(os.getenv('CI'), 'Skip real network tests in CI')
def test_jmv_get_album_detail_real(self):
    ...

This is an optional improvement since the project appears to intentionally run real network tests based on the existing test infrastructure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_jmcomic/test_jm_cli.py` around lines 96 - 134, The tests
test_jmv_get_album_detail_real, test_jmv_print_album_detail_real, and
test_jmv_print_truncates_real call self.client.get_album_detail (network) and
should be made optional in CI: import os and use
unittest.skipIf(os.getenv('CI'), 'Skip real network tests in CI') (or check a
custom env like JM_REAL_TESTS) to decorate each of those test methods (or wrap
the entire TestCase) so they are skipped when the CI env var is set; ensure
decorators are applied to the exact functions named above (or the TestCase
class) and add the necessary import for unittest and os.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/test_jmcomic/test_jm_cli.py`:
- Around line 37-42: The test function test_jmcomic_download_album unpacks a
second return value into dler but never uses it; rename that variable to _dler
(or simply _ ) where download_album is called (download_album(...,
downloader=JustDownloadSpecificCountImage)) to mark it intentionally unused and
silence lint warnings while leaving behavior unchanged.

---

Nitpick comments:
In `@src/jmcomic/cl.py`:
- Around line 131-159: The option path normalization in Jmcomic.parse_arg
duplicates logic from JmcomicUI.parse_arg; extract that into a shared helper
(e.g., _resolve_option_path(option_str: str) -> Optional[str]) that returns None
when option_str is empty or "''" and otherwise returns
os.path.abspath(option_str), then replace the duplicate block in parse_arg (and
update JmcomicUI.parse_arg to call the same helper) so both classes reuse the
single function for resolving option paths.

In `@tests/test_jmcomic/test_jm_cli.py`:
- Around line 96-134: The tests test_jmv_get_album_detail_real,
test_jmv_print_album_detail_real, and test_jmv_print_truncates_real call
self.client.get_album_detail (network) and should be made optional in CI: import
os and use unittest.skipIf(os.getenv('CI'), 'Skip real network tests in CI') (or
check a custom env like JM_REAL_TESTS) to decorate each of those test methods
(or wrap the entire TestCase) so they are skipped when the CI env var is set;
ensure decorators are applied to the exact functions named above (or the
TestCase class) and add the necessary import for unittest and os.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 70d474d9-ad80-437d-bff4-2a04a33403d5

📥 Commits

Reviewing files that changed from the base of the PR and between 16e5a00 and e812422.

📒 Files selected for processing (3)
  • src/jmcomic/cl.py
  • src/jmcomic/jm_toolkit.py
  • tests/test_jmcomic/test_jm_cli.py
✅ Files skipped from review due to trivial changes (1)
  • src/jmcomic/jm_toolkit.py

hect0x7

This comment was marked as off-topic.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
assets/docs/sources/tutorial/2_command_line.md (1)

7-10: Add language specifier to fenced code block.

For consistency with other code blocks in this document (which use sh), add a language specifier:

📝 Proposed fix
-```
+```sh
 # 下载album 123 456,下载photo 333。彼此之间使用空格间隔
 jmcomic 123 456 p333
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @assets/docs/sources/tutorial/2_command_line.md around lines 7 - 10, Add the
shell language specifier to the fenced code block containing the example command
("jmcomic 123 456 p333") so it matches other blocks using sh; locate the
backtick-fenced block that contains the text "# 下载album 123 456,下载photo
333。彼此之间使用空格间隔" and prefix the opening with `sh` (i.e., change to ```sh)
to enable proper syntax highlighting.


</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @assets/docs/sources/tutorial/2_command_line.md:

  • Around line 7-10: Add the shell language specifier to the fenced code block
    containing the example command ("jmcomic 123 456 p333") so it matches other
    blocks using sh; locate the backtick-fenced block that contains the text "#
    下载album 123 456,下载photo 333。彼此之间使用空格间隔" and prefix the opening with `sh` (i.e., change to ```sh) to enable proper syntax highlighting.

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: defaults

**Review profile**: CHILL

**Plan**: Pro

**Run ID**: `b23777c6-7bae-4cf1-ac58-8547795c4498`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between e812422a20d8ab56c49032b3e4e86d3c47c614c2 and dc4c139b83e911848338092f2341a014a19612f5.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `README.md`
* `assets/docs/sources/tutorial/2_command_line.md`
* `src/jmcomic/__init__.py`
* `src/jmcomic/cl.py`
* `tests/test_jmcomic/test_jm_cli.py`

</details>

<details>
<summary>✅ Files skipped from review due to trivial changes (2)</summary>

* src/jmcomic/__init__.py
* README.md

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

@hect0x7 hect0x7 merged commit 17fc58d into master Mar 22, 2026
6 of 9 checks passed
@hect0x7 hect0x7 deleted the dev branch March 22, 2026 10:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant