<div align="center" style="margin-top: 20px">
<a href="https://github.com/FelixSiegel/itagger">
<img src="assets/logos/logo.svg" alt="Logo" width="120" height="120" />
</a>
<h1 style="margin-top: 0">iTagger</h1>
</div>
[](https://www.python.org/)
[](https://github.com/astral-sh/uv)
[](https://github.com/astral-sh/ruff)
[](https://mypy-lang.org/)
A Python tool for creating `ComicInfo.xml` files for manga using the AniList API. This tool follows the ComicInfo schema specifications from [The Anansi Project](https://anansi-project.github.io/docs/comicinfo/documentation).
## Features
- 🔍 **Search manga** by title using AniList's comprehensive database
- 📊 **Fetch detailed metadata** including authors, artists, genres, tags, and more
- 📄 **Generate ComicInfo.xml** files compliant with the ComicInfo v2.1 schema
- 🎯 **Volume/chapter-specific** metadata generation
- 🌐 **Multi-language support** with proper language ISO codes
- 📚 **Batch processing** for multiple volumes
- 🎨 **Rich metadata** including character information, reading direction, age ratings
## Installation
### Using uv (Recommended)
Install the tool using [uv](https://docs.astral.sh/uv/):
```bash
uv tool install itagger
```
### Using pip
Install from PyPI using pip:
```bash
pip install itagger
```
Or install in development mode from source:
```bash
git clone https://github.com/FelixSiegel/itagger
cd itagger
pip install -e .
```
### Manual Installation
1. Clone or download this repository
2. Install dependencies:
```bash
pip install requests lxml click python-dateutil
```
or using uv, just run:
```bash
uv sync
```
## Usage
### Command Line Interface
The tool provides a CLI with several commands:
```text
batch Generate ComicInfo.xml files for multiple volumes of a manga...
embed Embed ComicInfo.xml metadata directly into CBZ files.
generate Generate ComicInfo.xml for a specific manga.
search Search for manga on AniList.
```
You can get help for each command using the `--help` flag.
#### Search for manga
Syntax:
```sh
itagger search [OPTIONS] QUERY
```
Example:
```bash
itagger search --limit 5 "Attack on Titan"
```
The command returns a list of matching manga titles along with their AniList IDs and other basic information.
#### Generate ComicInfo.xml for a specific manga (requires AniList ID)
Syntax:
```sh
itagger generate [OPTIONS] MANGA_ID
```
Example:
```bash
itagger generate --volume 1 --scan-info "My Scanlation Group" 105778
```
This command generates a `ComicInfo.xml` file for volume 1 of the manga with AniList ID `105778` (Chainsaw Man) and includes the specified [scanlation group](https://anansi-project.github.io/docs/comicinfo/documentation#scaninformation) in the metadata.
You can find the AniList ID using the `search` command or by visiting the manga's page on AniList (the ID is in the URL).
#### Batch generate for multiple volumes
Syntax:
```sh
itagger batch [OPTIONS] QUERY
```
Example:
```bash
itagger batch --volumes "1-10" --output-dir "output/" "One Piece"
```
This command searches for "One Piece" on AniList, retrieves its AniList ID, and generates `ComicInfo.xml` files for volumes 1 to 10, saving them in the specified output directory.
#### Embed metadata directly into CBZ files
Syntax:
```sh
itagger embed [OPTIONS] CBZ_DIR MANGA_ID
```
Examples:
```bash
# Embed metadata (from Manga by ID) into existing CBZ files for Komga/Kavita
itagger embed /path/to/cbz/folder 30933
# For specific range of volumes/chapters
itagger embed --range "1-10" /path/to/cbz/folder 30933
# For volume-based CBZ files
itagger embed --metadata-type volumes --pattern "v{:02d}.cbz" /path/to/cbz/folder 30933
# Dry run to see what would be processed
itagger embed --dry-run /path/to/cbz/folder 30933
```
Also read the [KOMGA_KAVITA_GUIDE.md](KOMGA_KAVITA_GUIDE.md) for more details on embedding metadata into CBZ files.
### Programmatic Usage
```python
from itagger.anilist_client import AniListClient
from itagger.comicinfo_generator import ComicInfoGenerator
# Initialize clients
client = AniListClient()
generator = ComicInfoGenerator()
# Search for manga
results = client.search_manga("Naruto", limit=5)
print(f"Found {len(results)} results")
# Get detailed information
manga = client.get_manga_details(results[0].id)
# Generate ComicInfo.xml
comic_info = generator.generate_comic_info(
manga=manga,
volume=1,
scan_info="Example Scanlation Group"
)
# Save to file
with open("ComicInfo.xml", "w", encoding="utf-8") as f:
f.write(comic_info)
```
## ComicInfo.xml Fields
The tool maps AniList data to ComicInfo.xml fields according to the schema:
| ComicInfo Field | Source | Description |
|----------------|--------|-------------|
| `Title` | Manga title + volume/chapter | Full title including volume/chapter info |
| `Series` | Primary title | Series name (English preferred, fallback to Romaji) |
| `Number` | Volume/Chapter | Volume or chapter number |
| `Count` | Volume count | Total volumes in series |
| `Volume` | Volume number | Specific volume number |
| `Summary` | Description | Cleaned description without HTML |
| `Year/Month/Day` | Start date | Publication start date |
| `Writer` | Staff with "Story" role | Authors/writers |
| `Penciller` | Staff with "Art" role | Artists |
| `Publisher` | Studios | Publishing studios |
| `Genre` | Genres | Comma-separated genres |
| `Tags` | Tags (non-spoiler) | Comma-separated tags |
| `Web` | Site URL | AniList page URL |
| `LanguageISO` | Country of origin | Language code (ja, ko, zh, etc.) |
| `Manga` | Country + format | Reading direction (YesAndRightToLeft for JP) |
| `Characters` | Main characters | Main character names |
| `AgeRating` | Tags + adult flag | Age appropriateness rating |
| `CommunityRating` | Average score | Rating converted to 0-5 scale |
### Example Output
```xml
<?xml version='1.0' encoding='UTF-8'?>
<ComicInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ComicInfo.xsd">
<Title>Attack on Titan Volume 1</Title>
<Series>Attack on Titan</Series>
<Number>1</Number>
<Count>34</Count>
<Volume>1</Volume>
<Summary>Hundreds of years ago, horrifying creatures which resembled humans appeared...</Summary>
<Notes>Generated from AniList ID: 53390</Notes>
<Year>2009</Year>
<Month>9</Month>
<Day>9</Day>
<Writer>Hajime Isayama</Writer>
<Penciller>Hajime Isayama</Penciller>
<CoverArtist>Hajime Isayama</CoverArtist>
<Publisher>Kodansha</Publisher>
<Genre>Action, Drama, Fantasy, Horror</Genre>
<Tags>Survival, Military, Tragedy, Gore, War</Tags>
<Web>https://anilist.co/manga/53390</Web>
<LanguageISO>ja</LanguageISO>
<Format>Digital</Format>
<BlackAndWhite>Yes</BlackAndWhite>
<Manga>YesAndRightToLeft</Manga>
<Characters>Eren Yeager, Mikasa Ackerman, Armin Arlert</Characters>
<MainCharacterOrTeam>Eren Yeager</MainCharacterOrTeam>
<AgeRating>Mature 17+</AgeRating>
<CommunityRating>4.3</CommunityRating>
</ComicInfo>
```
## AniList API Integration
The tool uses AniList's GraphQL API to fetch comprehensive manga metadata:
- **Search**: Finds manga by title with popularity and score sorting
- **Details**: Retrieves full metadata including staff, characters, tags, and more
- **No authentication required**: Uses public API endpoints
- **Rate limiting**: Respectful API usage with proper error handling
## Schema Compliance
Generated ComicInfo.xml files follow the [ComicInfo v2.1 schema](https://anansi-project.github.io/docs/comicinfo/schemas/v2.1) specifications:
- Proper XML structure and encoding
- Correct data types for numeric fields
- Enumerated values for specific fields (Manga, AgeRating, etc.)
- Optional field handling
- HTML entity escaping
## Dependencies
- `requests`: HTTP client for AniList API
- `lxml`: XML processing and generation
- `click`: Command-line interface framework
- `python-dateutil`: Date parsing utilities
## License
This project is open source and available under the [GNU General Public License v3.0](LICENSE).
## Contributing
Contributions are welcome! Please feel free to submit issues, feature requests, or pull requests.
## Acknowledgments
- [AniList](https://anilist.co/) for providing the comprehensive manga database API
- [The Anansi Project](https://anansi-project.github.io/) for ComicInfo schema documentation
- [uv](https://docs.astral.sh/uv/) for making the whole build and installation process easier
- The manga and digital comics community for standardization efforts
Raw data
{
"_id": null,
"home_page": null,
"name": "itagger",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.13",
"maintainer_email": null,
"keywords": "anilist, cbz, comicinfo, kavita, komga, manga, metadata",
"author": null,
"author_email": "InfinityCoding <infinitycoding@web.de>",
"download_url": "https://files.pythonhosted.org/packages/5a/a9/86ec3ce1201ca3b281e3f59b32ecff2ae3b5b8a50581c7f7db52a673fc9f/itagger-0.1.2.tar.gz",
"platform": null,
"description": "<div align=\"center\" style=\"margin-top: 20px\">\n <a href=\"https://github.com/FelixSiegel/itagger\">\n <img src=\"assets/logos/logo.svg\" alt=\"Logo\" width=\"120\" height=\"120\" />\n </a>\n <h1 style=\"margin-top: 0\">iTagger</h1>\n</div>\n\n[](https://www.python.org/)\n[](https://github.com/astral-sh/uv)\n[](https://github.com/astral-sh/ruff)\n[](https://mypy-lang.org/)\n\nA Python tool for creating `ComicInfo.xml` files for manga using the AniList API. This tool follows the ComicInfo schema specifications from [The Anansi Project](https://anansi-project.github.io/docs/comicinfo/documentation).\n\n## Features\n\n- \ud83d\udd0d **Search manga** by title using AniList's comprehensive database\n- \ud83d\udcca **Fetch detailed metadata** including authors, artists, genres, tags, and more\n- \ud83d\udcc4 **Generate ComicInfo.xml** files compliant with the ComicInfo v2.1 schema\n- \ud83c\udfaf **Volume/chapter-specific** metadata generation\n- \ud83c\udf10 **Multi-language support** with proper language ISO codes\n- \ud83d\udcda **Batch processing** for multiple volumes\n- \ud83c\udfa8 **Rich metadata** including character information, reading direction, age ratings\n\n## Installation\n\n### Using uv (Recommended)\n\nInstall the tool using [uv](https://docs.astral.sh/uv/):\n\n```bash\nuv tool install itagger\n```\n\n### Using pip\n\nInstall from PyPI using pip:\n\n```bash\npip install itagger\n```\n\nOr install in development mode from source:\n\n```bash\ngit clone https://github.com/FelixSiegel/itagger\ncd itagger\npip install -e .\n```\n\n### Manual Installation\n\n1. Clone or download this repository\n2. Install dependencies:\n\n```bash\npip install requests lxml click python-dateutil\n```\n\nor using uv, just run:\n\n```bash\nuv sync\n```\n\n## Usage\n\n### Command Line Interface\n\nThe tool provides a CLI with several commands:\n\n```text\n batch Generate ComicInfo.xml files for multiple volumes of a manga...\n embed Embed ComicInfo.xml metadata directly into CBZ files.\n generate Generate ComicInfo.xml for a specific manga.\n search Search for manga on AniList.\n```\n\nYou can get help for each command using the `--help` flag.\n\n#### Search for manga\n\nSyntax:\n\n```sh\nitagger search [OPTIONS] QUERY\n```\n\nExample:\n\n```bash\nitagger search --limit 5 \"Attack on Titan\"\n```\n\nThe command returns a list of matching manga titles along with their AniList IDs and other basic information.\n\n#### Generate ComicInfo.xml for a specific manga (requires AniList ID)\n\nSyntax:\n\n```sh\nitagger generate [OPTIONS] MANGA_ID\n```\n\nExample:\n\n```bash\nitagger generate --volume 1 --scan-info \"My Scanlation Group\" 105778\n```\n\nThis command generates a `ComicInfo.xml` file for volume 1 of the manga with AniList ID `105778` (Chainsaw Man) and includes the specified [scanlation group](https://anansi-project.github.io/docs/comicinfo/documentation#scaninformation) in the metadata.\n\nYou can find the AniList ID using the `search` command or by visiting the manga's page on AniList (the ID is in the URL).\n\n#### Batch generate for multiple volumes\n\nSyntax:\n\n```sh\nitagger batch [OPTIONS] QUERY\n```\n\nExample:\n\n```bash\nitagger batch --volumes \"1-10\" --output-dir \"output/\" \"One Piece\"\n```\n\nThis command searches for \"One Piece\" on AniList, retrieves its AniList ID, and generates `ComicInfo.xml` files for volumes 1 to 10, saving them in the specified output directory.\n\n#### Embed metadata directly into CBZ files\n\nSyntax:\n\n```sh\nitagger embed [OPTIONS] CBZ_DIR MANGA_ID\n```\n\nExamples:\n\n```bash\n# Embed metadata (from Manga by ID) into existing CBZ files for Komga/Kavita\nitagger embed /path/to/cbz/folder 30933\n\n# For specific range of volumes/chapters\nitagger embed --range \"1-10\" /path/to/cbz/folder 30933\n\n# For volume-based CBZ files\nitagger embed --metadata-type volumes --pattern \"v{:02d}.cbz\" /path/to/cbz/folder 30933\n\n# Dry run to see what would be processed\nitagger embed --dry-run /path/to/cbz/folder 30933\n```\n\nAlso read the [KOMGA_KAVITA_GUIDE.md](KOMGA_KAVITA_GUIDE.md) for more details on embedding metadata into CBZ files.\n\n### Programmatic Usage\n\n```python\nfrom itagger.anilist_client import AniListClient\nfrom itagger.comicinfo_generator import ComicInfoGenerator\n\n# Initialize clients\nclient = AniListClient()\ngenerator = ComicInfoGenerator()\n\n# Search for manga\nresults = client.search_manga(\"Naruto\", limit=5)\nprint(f\"Found {len(results)} results\")\n\n# Get detailed information\nmanga = client.get_manga_details(results[0].id)\n\n# Generate ComicInfo.xml\ncomic_info = generator.generate_comic_info(\n manga=manga,\n volume=1,\n scan_info=\"Example Scanlation Group\"\n)\n\n# Save to file\nwith open(\"ComicInfo.xml\", \"w\", encoding=\"utf-8\") as f:\n f.write(comic_info)\n```\n\n## ComicInfo.xml Fields\n\nThe tool maps AniList data to ComicInfo.xml fields according to the schema:\n\n| ComicInfo Field | Source | Description |\n|----------------|--------|-------------|\n| `Title` | Manga title + volume/chapter | Full title including volume/chapter info |\n| `Series` | Primary title | Series name (English preferred, fallback to Romaji) |\n| `Number` | Volume/Chapter | Volume or chapter number |\n| `Count` | Volume count | Total volumes in series |\n| `Volume` | Volume number | Specific volume number |\n| `Summary` | Description | Cleaned description without HTML |\n| `Year/Month/Day` | Start date | Publication start date |\n| `Writer` | Staff with \"Story\" role | Authors/writers |\n| `Penciller` | Staff with \"Art\" role | Artists |\n| `Publisher` | Studios | Publishing studios |\n| `Genre` | Genres | Comma-separated genres |\n| `Tags` | Tags (non-spoiler) | Comma-separated tags |\n| `Web` | Site URL | AniList page URL |\n| `LanguageISO` | Country of origin | Language code (ja, ko, zh, etc.) |\n| `Manga` | Country + format | Reading direction (YesAndRightToLeft for JP) |\n| `Characters` | Main characters | Main character names |\n| `AgeRating` | Tags + adult flag | Age appropriateness rating |\n| `CommunityRating` | Average score | Rating converted to 0-5 scale |\n\n### Example Output\n\n```xml\n<?xml version='1.0' encoding='UTF-8'?>\n<ComicInfo xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"ComicInfo.xsd\">\n <Title>Attack on Titan Volume 1</Title>\n <Series>Attack on Titan</Series>\n <Number>1</Number>\n <Count>34</Count>\n <Volume>1</Volume>\n <Summary>Hundreds of years ago, horrifying creatures which resembled humans appeared...</Summary>\n <Notes>Generated from AniList ID: 53390</Notes>\n <Year>2009</Year>\n <Month>9</Month>\n <Day>9</Day>\n <Writer>Hajime Isayama</Writer>\n <Penciller>Hajime Isayama</Penciller>\n <CoverArtist>Hajime Isayama</CoverArtist>\n <Publisher>Kodansha</Publisher>\n <Genre>Action, Drama, Fantasy, Horror</Genre>\n <Tags>Survival, Military, Tragedy, Gore, War</Tags>\n <Web>https://anilist.co/manga/53390</Web>\n <LanguageISO>ja</LanguageISO>\n <Format>Digital</Format>\n <BlackAndWhite>Yes</BlackAndWhite>\n <Manga>YesAndRightToLeft</Manga>\n <Characters>Eren Yeager, Mikasa Ackerman, Armin Arlert</Characters>\n <MainCharacterOrTeam>Eren Yeager</MainCharacterOrTeam>\n <AgeRating>Mature 17+</AgeRating>\n <CommunityRating>4.3</CommunityRating>\n</ComicInfo>\n```\n\n## AniList API Integration\n\nThe tool uses AniList's GraphQL API to fetch comprehensive manga metadata:\n\n- **Search**: Finds manga by title with popularity and score sorting\n- **Details**: Retrieves full metadata including staff, characters, tags, and more\n- **No authentication required**: Uses public API endpoints\n- **Rate limiting**: Respectful API usage with proper error handling\n\n## Schema Compliance\n\nGenerated ComicInfo.xml files follow the [ComicInfo v2.1 schema](https://anansi-project.github.io/docs/comicinfo/schemas/v2.1) specifications:\n\n- Proper XML structure and encoding\n- Correct data types for numeric fields\n- Enumerated values for specific fields (Manga, AgeRating, etc.)\n- Optional field handling\n- HTML entity escaping\n\n## Dependencies\n\n- `requests`: HTTP client for AniList API\n- `lxml`: XML processing and generation\n- `click`: Command-line interface framework\n- `python-dateutil`: Date parsing utilities\n\n## License\n\nThis project is open source and available under the [GNU General Public License v3.0](LICENSE).\n\n## Contributing\n\nContributions are welcome! Please feel free to submit issues, feature requests, or pull requests.\n\n## Acknowledgments\n\n- [AniList](https://anilist.co/) for providing the comprehensive manga database API\n- [The Anansi Project](https://anansi-project.github.io/) for ComicInfo schema documentation\n- [uv](https://docs.astral.sh/uv/) for making the whole build and installation process easier\n- The manga and digital comics community for standardization efforts\n",
"bugtrack_url": null,
"license": null,
"summary": "CLI tool to create and embed ComicInfo.xml metadata for manga using AniList API",
"version": "0.1.2",
"project_urls": {
"Homepage": "https://github.com/FelixSiegel/itagger",
"Issues": "https://github.com/FelixSiegel/itagger/issues",
"Repository": "https://github.com/FelixSiegel/itagger"
},
"split_keywords": [
"anilist",
" cbz",
" comicinfo",
" kavita",
" komga",
" manga",
" metadata"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "d78b13271b214bea88e70944e61836937d1ce2d6a0426f2583b5478f7bdb2c29",
"md5": "fea09294a9520c2b6adb8fcc37cf5c3e",
"sha256": "fb4689c38de15263b0688357b09ec0477f336ca769cef8bfd7630c055beb1479"
},
"downloads": -1,
"filename": "itagger-0.1.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "fea09294a9520c2b6adb8fcc37cf5c3e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.13",
"size": 27668,
"upload_time": "2025-10-18T16:40:38",
"upload_time_iso_8601": "2025-10-18T16:40:38.064426Z",
"url": "https://files.pythonhosted.org/packages/d7/8b/13271b214bea88e70944e61836937d1ce2d6a0426f2583b5478f7bdb2c29/itagger-0.1.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "5aa986ec3ce1201ca3b281e3f59b32ecff2ae3b5b8a50581c7f7db52a673fc9f",
"md5": "806299c2251e06c062517b67a1455c32",
"sha256": "0bae8bcbc43f513213e126cb33cbb691ad6b08507ebd321ceaf1b03e7dee651d"
},
"downloads": -1,
"filename": "itagger-0.1.2.tar.gz",
"has_sig": false,
"md5_digest": "806299c2251e06c062517b67a1455c32",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.13",
"size": 36805,
"upload_time": "2025-10-18T16:40:39",
"upload_time_iso_8601": "2025-10-18T16:40:39.276366Z",
"url": "https://files.pythonhosted.org/packages/5a/a9/86ec3ce1201ca3b281e3f59b32ecff2ae3b5b8a50581c7f7db52a673fc9f/itagger-0.1.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-18 16:40:39",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "FelixSiegel",
"github_project": "itagger",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "requests",
"specs": [
[
">=",
"2.31.0"
]
]
},
{
"name": "lxml",
"specs": [
[
">=",
"4.9.0"
]
]
},
{
"name": "click",
"specs": [
[
">=",
"8.1.0"
]
]
},
{
"name": "python-dateutil",
"specs": [
[
">=",
"2.8.0"
]
]
}
],
"lcname": "itagger"
}