Contact information
Name: Md Waqib Sk
Matrix/nick: waqib2992
Mail: waqibsk.2006@gmail.com
Github: Waqibsk (Md Waqib Sk) · GitHub
LinkedIn: https://www.linkedin.com/in/md-waqib-sk-0619232a2/
Project Size : 175 Hours (medium)
Proposed project
My goal is to make a Calibre plugin for BookBrainz . The UI will contain 3 different tabs, each serving different purpose.
- Update Metadata
- Browse BookBrainz
- Add Collection
File Structure :
calibbre/
├── images/
│ └── logo.png
│
├── config.py
├── __init__.py
├── main.py
├── ui.py
└── plugin-import-name-calibbre.txt
UI Development
I will be using PyQt5 and Qt Designer to develop the UI . It will generate a *.ui file then i will convert it to a .*.py file using pyuic5 for further development .
Features :
( Note: I have tried to give sample functions where needed and mostly i have written the pseudo code . If any of them require further clarification , please let me know )
MetaData Update of a selected calibre book:
- Get the book that the user selected (if no book is selected this option will be disabled)
Api Endpoints :
db.get_metadata()gui.library_view
selected_ids = self.gui.library_view.get_selected_ids()
if not selected_ids:
print("No books selected.")
# disable the buttons (add a message suggesting the user to select a book first)
return
else:
# Enable buttons
db = self.gui.current_db.new_api
first_book_id = selected_ids[0] # we will only fetch metadata of one book
self.mi = db.get_metadata(first_book_id)
-
Search by the name of the book (NOTE: the search will be in entityType: Edition in BB) :
Api Endpoint : https://api.bookbrainz.org/1/search?q=Alchemist&type=edition&size=10&from=0
Sample Structure :
Sample Function :
def search_book(self):
"""
1. Search for the book by name and get 0-20 results.
2. Populate the table.
"""
book_name = self.mi.title
search_url = f"https://api.bookbrainz.org/1/search?q={book_name}&type=edition&size=10&from=0"
response = requests.get(search_url)
search_response_data = response.json()
results = search_response_data.get("searchResult", [])
# get required fields from search_response_data (bookBBID, bookTitle etc)
for index,item in enumerate(results):
bookBBID = item.get("bbid")
bookTitle = item["defaultAlias"].get("name",book_name)
row_position = self.tableWidget.rowCount()
self.tableWidget.insertRow(row_position)
self.tableWidget.setItem(row_position, 0, QTableWidgetItem(bookBBID))
self.tableWidget.setItem(row_position, 1, QTableWidgetItem(bookTitle))
# same for the others
A list of 20 search (we can make it customizable by having a settings feature but i think its unnecessary ) result will appear In a tabular format with
-
Book BBID
-
Book name
-
Sorted book name
-
Book language
-
Book author (possible after closing the ticket)
There will be different states also like
- Initial state
- Loading state
- All results fetched
I will handle these states using stacked QtWidget and change the ui accordingly
Demo Screenshots :
-
Get selected book’s metadata :
Api Endpoint : https://api.bookbrainz.org/1/edition/{selected_bbid}"
Response Structure :
Sample Function :
def fetch_metadata(): # get the current selected bbid selected_row = self.tableWidget.currentRow() if selected_row == -1: selected_row=0 selected_bbid = self.tableWidget.item(selected_row,0).text() book_name = self.tableWidget.item(selected_row,1).text() # get the data from api and show in ui try: detail_url = f"https://api.bookbrainz.org/1/edition/{selected_bbid}" response = requests.get(detail_url) response.raise_for_status() edition_data = response.json() except Exception as e: QMessageBox.critical(self, "API Error", f"Failed to fetch details: {e}") return # convert to Calibre metadata object and cache it self.cached_metadata = self.create_metadata_object(edition_data, book_name)- Update metadata :
from calibre.ebooks.metadata.book.base import Metadata def create_metadata_object(self, bb_data,book_name): # getting the details title = book_name authors = [] if 'authorCredits' in bb_data: for credit in bb_data['authorCredits']: if 'name' in credit and isinstance(credit['name'], dict): authors.append(credit['name'].get('name', 'Unknown')) elif 'name' in credit: authors.append(credit['name']) if not authors: authors = ["Unknown Author"] # metadata object mi = Metadata(title,authors) # set publisher and date and other details also to mi return mi
def update_metadata():
db = self.gui.current_db.new_api
db.set_metadata(book_id, self.cached_metadata, set_title=True, set_authors=True)
QMessageBox.information(self, "Success", "Book updated successfully!")
# clear cache after update
self.cached_metadata = None
Browse BookBrainz Tab :
This will contain a search bar where users will search editions by name .
( Note : Then again 0-20 search result will appear with book Name and author name as the previous tab i initially thought of merging this to a single tab because it is a bit repitative but In my opinion keeping the update metadata separate will be clean as the 2 tabs serve different purposes even though they share the same table )
This will contain two features :
- Add book to calibre
We will be adding the books in calibre as empty books (will buy/read later) because we will only provide metadata not the actual readable book
def get_selected_books_data():
results=[] # list of tuple ( format <mi,format_map> )
# fetch the selected books metadata by bbid from BB api
# create metadata object
# append result as <mi,{}> (as we are adding empty book the file path should be empty)
self.books_data_to_add=results
def add_book_to_calibre():
if not self.books_data_to_add:
return
# use the db.add_books() endpoint with books_data_to_add
ids = self.gui.current_db.add_books(self.books_data_to_add, add_duplicates=True)
print(f"Successfully added {len(ids)} empty books.")
- Download Book MetaData as Json file (single or multiple) :
def download_book_metadata(self):
save_path = choose_save_file(self.gui, 'save-json', 'books.json', filters= [('JSON Files', ['json'])]) # choose where to save
export_data = {} # array of selected books metadata
if save_path:
with open(save_path, 'w', encoding='utf-8') as json_file:
json.dump(export_data, json_file, indent=4, ensure_ascii=False)
print(f"Successfully exported {len(export_data)} books to {save_path}")
Add Collection To Calibre :
-
Search public Collection by BBID
-
Show Collection items in the ui
-
User may choose some book or entire collection to add in calibre
( I need some opinion regarding this )
-
Initially i thought of merging this feature with the browse tab because in both cases we are adding the books to calibre
-
But In future we may introduce Auth and private collections so i decided to make a separate tab to keep things clean
-
I think we should restrict the collection size to 50-100 because it does not make sense to show 500 books from the collection and show it in the UI
Note: The API required to implement this feature is not currently present but i think it can implemented in the timeline before i start working on this (thats why i saved this feature for last ) , I am sharing the details of what the API endpoint should look like .
( I want to know if the team will handle this otherwise i will allocate one more week dedicating to this )
Api Endpoint : /collection/{bbid} (with a type property )
Usage:
- Returns the collection , along with a items array
- if in the array item itself we get the metadata details for each item , it would be great . Because then while adding the editions in calibre i will not have to make separate API calls for getting metadata info for each selected item
Example on how it will be used in the function :
-
Fetch Collection By BBID :
def fetch_collection(): search_result = requests.url(api) # check if results array size is greater than 50 if( len(search_result.items) > 50 ): # show error message # show the items in tabular format -
Add Collection function :
def add_collection(): # it will be almost similar to add_book_to_calibre function in Browse tab
Timeline
Phase 1: (Before Mid-Evals)
Expected outcome :
- A working Update Metadata tab
- Start working on Browse tab
Week 1 (May 25 - May 31):
- Create & finalize Qt ui design for Metadata tab
- Create & finalize Qt ui design for Browse tab
Week 2 (June 1 - June 7):
- Write & test function for searching selected book in Bookbrainz
- Show the books in the ui and handle different states (loading etc)
Week 3 (June 8 - June 14):
- Implement & test Fetch Metadata from BB function and display it in the UI
- Handle different states in the ui while fetching
Week 4-5 (June 15 - June 28):
- Make and test the Update book Metadata function
- (Work on update metadata tab will be finished )
- Test entire update metadata feature and fix any existing bugs
- Write documentation on how to use it
Week 6 (June 29 - July 5):
- ( Work on Browse BB tab starts )
- Implement & test search Edition ( by name ) in calibre function
Phase 2: (After Mid-Evals)
Expected outcome :
- A Working installable calibre plugin with 3 different tabs/features
Week 7-8 (July 6 - July 19):
- Write and test Download Metadata function
- Write and test Add Book to Calibre function
- Test entire Browse tab
- Write documentation on how to use it
- (Work on Browse tab will be finished )
Week 9 (July 20 - July 26):
- (Start working on Collection tab )
- Create & finalize Qt ui design for Collection tab
- Implement & test function to fetch public collection( using bbid) from BB
- Show the results in tabular format & handle different states
Week 10 (July 27 - Aug 2):
- Write and test add collection to calibre function (it will be almost simillar to add book to calibre in browse tab)
- Write docs for Collection tab and test the entire tab
- (Work on Add Collections tab will be finished )
Week 11 (Aug 3 - Aug 9):
- This is buffer time if any of the fixed deadlines are not met i will cover it here
- Test the entire plugin
Week 12 (Aug 10 - Aug 16):
- Wrap up and make video describing all the features of the plugin
- Final Report
In all the functions i will implement proper error handling with QtMessageBox (eg: Network error , No books found etc )
Extended/Future Goals
( All the features mentioned below will require Authentication )
-
Add Books To BookBrainz : Add calibre books directly to BookBrainz
- I think This would be a good feature to grow the books in Book Brainz as users will just have to press one click to add the book ( the calibre books will be stored as editions in book brainz)
-
Add Private Collection :
- It will be an Extension of the Collection tab
-
CritiqueBrainz integration ( future feature ) : Write reviews of a selected book directly from the plugin
- In this case first we have to check if the selected book exists in BB or not , then get the corresponding BBID after that we can make the request
Community affinities
What type of music do you listen to?
I listen to mostly soft music , listing some of my favourite songs :
- Lego House : Release group “Lego House” by Ed Sheeran - MusicBrainz
- Perfect : Release group “Perfect” by Ed Sheeran - MusicBrainz
- Photograph : Release group “Photograph” by Ed Sheeran - MusicBrainz
- Circles : Release group “Circles” by Post Malone - MusicBrainz
- Gul : Release group “Gul” by Anuv Jain - MusicBrainz
- Alag Aasmaan : Release group “Alag Aasmaan” by Anuv Jain - MusicBrainz
What type of books do you read?
Some of my favourite books are :
- The Alchemist : The Alchemist (Work) – BookBrainz
- The Da Vinci Code : The Da Vinci Code (Edition) – BookBrainz
- Harry Potter : Harry Potter (Series) – BookBrainz
What aspects of MusicBrainz/ListenBrainz/BookBrainz/Picard interest you the most?
I always liked the classic vibe of Wikipedia and thats exactly what i get when i look at Metabrainz projects . I also like how detailed the databases are ( i mean i never thought a single book can have so much info before looking at bookbrainz).
Programming precedents
When did you first start programming?
I started programming in the first year of my college . And the initial motivation for programming was i can make games through it . Although my interest has changed over the time but that was how i started .
Have you contributed to other open source projects? If so, which projects and can we see some of your code?
Yes i have contributed to OpenStreetMap
You can check out the PRs here:
- Validation: non standard dashes in dash-sensitive tag values by Waqibsk · Pull Request #11316 · openstreetmap/iD · GitHub
- Feat: Click to switch features that are crossing ways by Waqibsk · Pull Request #11346 · openstreetmap/iD · GitHub
- fix: allow 0 as input value in layer tag by Waqibsk · Pull Request #11300 · openstreetmap/iD · GitHub
And some no-code (docs) contribution include :
- Next-cloudinary : add: guide for delivering remote images by Waqibsk · Pull Request #600 · cloudinary-community/next-cloudinary · GitHub
- LocalStack : [ Docs]: Normalize structure: Build & deploy Lambda container images (ECR) by Waqibsk · Pull Request #260 · localstack/localstack-docs · GitHub
You can also checkout some of my personal projects :
- GitHub - Waqibsk/Darklash: 2d fighting game
- GitHub - Waqibsk/delta: A Go script to delete your emails based on their subjects :)
Practical requirements
What computer(s) do you have available for working on your SoC project?
I have Asus Vivobook 14
Specifications:
- Processor: AMD Ryzen™ 5 7520U with Radeon™ Graphics × 8
- Os: Ubuntu 24.04.1 LTS
- Ram: 8.0 GB
- Disk Space: 512 GB
How much time do you have available per week, and how would you plan to use it?
I have no other commitments during the summer and can dedicate 20-25 hours a week.
I am also planning to close this ticket before the coding period .





