Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:58:23 +08:00
commit b8dc705a95
11 changed files with 1061 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
---
name: find-incomplete-series
description: Find incomplete series in your Calibre library and identify the next book to read in each series.
---
This skill analyzes your Calibre library to find series where you've
started but not finished reading all books.
To complete this task, run the following command:
```bash
python3 __SKILL_DIR__/scripts/series.py
```
This script will:
1. Query your Calibre library for all books that are part of a series
2. Exclude archived books
3. Identify series where you've read at least one book but haven't finished the entire series
4. Display the next unread book in each incomplete series
Important! Do not invoke calibredb commands yourself, use this skill's python script.
Important! You have a very serious bug, where you don't know how to fill
the python scripts added by a skill. You must look in the "scripts"
folder of where this SKILL.md is located!!

View File

@@ -0,0 +1,65 @@
#!/usr/bin/env python3
import subprocess
import json
from collections import defaultdict
# Query Calibre for all books in series
result = subprocess.run([
'/Applications/calibre.app/Contents/MacOS/calibredb', 'list',
'--with-library=http://killington.home.bitbin.de:8454/#',
'--username=calibre',
'--password=calibre',
'--fields=title,authors,series,series_index,*read,*archived',
'--search=series:true',
'--for-machine'
], capture_output=True, text=True)
books = json.loads(result.stdout)
# Group books by series, excluding archived books
series_dict = defaultdict(list)
for book in books:
if book.get('series') and not book.get('*archived'):
series_dict[book['series']].append(book)
# Sort books in each series by series_index
for series in series_dict:
series_dict[series].sort(key=lambda x: float(x.get('series_index', 0)))
# Find series with at least one read book but not all read
incomplete_series = []
for series, books_list in series_dict.items():
read_count = sum(1 for b in books_list if b.get('*read') == True)
total_count = len(books_list)
if read_count > 0 and read_count < total_count:
# Find the next unread book
next_unread = None
for book in books_list:
if not book.get('*read'):
next_unread = book
break
incomplete_series.append({
'series': series,
'read': read_count,
'total': total_count,
'next_book': next_unread
})
# Sort by series name
incomplete_series.sort(key=lambda x: x['series'])
# Output results
print(f"\n# UNFINISHED SERIES\n")
print(f"You have {len(incomplete_series)} incomplete series:\n")
for i, s in enumerate(incomplete_series, 1):
print(f"{i}. **{s['series']}** ({s['read']}/{s['total']} books read)")
if s['next_book']:
authors = s['next_book'].get('authors', 'Unknown')
title = s['next_book'].get('title', 'Unknown')
index = s['next_book'].get('series_index', 0)
print(f" Next: **{title}** by {authors} (Book #{index})\n")
else:
print(f" Next: Unable to determine\n")