diff --git a/README.md b/README.md index e85a798..522552d 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ pip install audiobook ```python from audiobook import AudioBook -ab = AudioBook() # argument: Speech-Speed="slow/normal/fast" +ab = AudioBook(speed="normal") # argument: Speech-Speed="slow/normal/fast" ab.save_audio(file_path, password=None) # save audio file ab.read_book(file_path, password=None) # listen to the book @@ -71,15 +71,18 @@ sudo apt update && sudo apt install espeak ffmpeg libespeak1 ## Project status -## V1.0.0 +This project is currently in development. Any contributions are welcome. + +## Changelog + +**V2.0.0** - [x] Save Audio Book locally - [x] Listen to the book - [x] Speech-speed control -- [x] Read password protected PDF -- [x] Create json file for the book - -## Upcoming Features +- [x] Read password-protected PDF +- [x] Create JSON file for the book +- [ ] Change the voice of the narrator - [ ] Support more extensions diff --git a/audiobook/main.py b/audiobook/main.py index f8165af..e4c7a8b 100644 --- a/audiobook/main.py +++ b/audiobook/main.py @@ -6,34 +6,55 @@ logger = logging.getLogger("PyPDF2") logger.setLevel(logging.INFO) +supported_file_types = (".pdf", ".txt") + speed_dict = { "slow": 100, "normal": 150, "fast": 200} -def speak_text(engine, text, print=False): - if print: +def speak_text(engine, text, display=True): + if display: print(text) engine.say(text) engine.runAndWait() class AudioBook: + """ + AudioBook class + + methods: + file_check: checks if file exists + pdf_to_json: converts pdf to json format + create_json_book: Creates json book from input file by calling respective method + save_audio: saves audio files in folder + read_book: reads the book + + sample usage: + ab = AudioBook(speed="normal") + ab.read_book(file_path, password="abcd") + """ + def __init__(self, speed="normal"): self.engine = pyttsx3.init() self.engine.setProperty("rate", speed_dict[speed]) - - def create_json_book(self, pdf_file_path, password=None): - - if not os.path.exists(pdf_file_path): + + def file_check(self, file_path): + """ + checks file format and if file exists + """ + if not os.path.exists(file_path): raise FileNotFoundError("File not found!") - - if not pdf_file_path.endswith(".pdf"): - raise ValueError("File must be a pdf!") - - book_dict = {} - with open(pdf_file_path, "rb") as fp: + + if not file_path.endswith(supported_file_types): + raise ValueError("File format not supported!") + + def pdf_to_json(self, input_file_path, password=None): + """ sub method to create json book from pdf file""" + json_book = {} + with open(input_file_path, "rb") as fp: pdfReader = PyPDF2.PdfFileReader(fp) if pdfReader.isEncrypted: logging.info("File is encrypted, trying to decrypt...") @@ -42,76 +63,83 @@ def create_json_book(self, pdf_file_path, password=None): for num in range(0, pages): pageObj = pdfReader.getPage(num) text = pageObj.extractText() - book_dict[num] = text - return book_dict, pages + json_book[num] = text + return json_book, pages - def save_audio(self, pdf_file_path, password=None): - if not os.path.exists(pdf_file_path): - raise FileNotFoundError("File not found!") + def txt_to_json(self, input_file_path): + """ sub method to create json book from txt file """ + json_book = {} + with open(input_file_path, "r") as fp: + file_data = fp.read() - if not pdf_file_path.endswith(".pdf"): - raise ValueError("File must be a pdf!") + # split text into pages of 2000 characters + for i in range(0, len(file_data), 2000): + json_book[i] = file_data[i:i+2000] + return json_book, len(json_book) - with open(pdf_file_path, "rb") as fp: - basename = os.path.basename(pdf_file_path).split(".")[0] - os.makedirs(basename, exist_ok=True) - logging.info('Saving audio files in folder: {}'.format(basename)) - pdfReader = PyPDF2.PdfFileReader(fp) - if pdfReader.isEncrypted: - logging.info("File is encrypted, trying to decrypt...") - pdfReader.decrypt(password) - pages = pdfReader.numPages - for num in range(0, pages): - pageObj = pdfReader.getPage(num) - text = pageObj.extractText() - self.engine.save_to_file(text, os.path.join(basename, basename + "_" + (str(num) + ".mp3"))) - self.engine.runAndWait() - - def read_book(self, pdf_file_path, password=None): - if not os.path.exists(pdf_file_path): - raise FileNotFoundError("File not found!") + def create_json_book(self, input_file_path, password=None): + """ method to create json book from input file + it calls respective method based on file format """ + self.file_check(input_file_path) + if input_file_path.endswith(".pdf"): + json_book, pages = self.pdf_to_json(input_file_path, password) + elif input_file_path.endswith(".txt"): + json_book, pages = self.txt_to_json(input_file_path) + return json_book, pages + + + def save_audio(self, input_file_path, password=None): + """ method to save audio files in folder """ + self.file_check(input_file_path) + logging.info("Creating your audiobook... Please wait...") + json_book, pages = self.create_json_book(input_file_path, password) - if not pdf_file_path.endswith(".pdf"): - raise ValueError("File must be a pdf!") + book_name = os.path.basename(input_file_path).split(".")[0] + os.makedirs(book_name, exist_ok=True) + logging.info('Saving audio files in folder: {}'.format(book_name)) - with open(pdf_file_path, "rb") as fp: - pdfReader = PyPDF2.PdfFileReader(fp) - if pdfReader.isEncrypted: - logging.info("File is encrypted, trying to decrypt...") - pdfReader.decrypt(password) - pages = pdfReader.numPages - speak_text(self.engine, f"The book has total {str(pages)} pages!") - speak_text(self.engine, "Please enter the page number: ") - start_page = int(input("Please enter the page number: ")) - 1 - reading = True - while reading: - if start_page > pages or start_page < 0: - speak_text(self.engine, "Invalid page number!") - speak_text(self.engine, f"The book has total {str(pages)} pages!") - start_page = int(input("Please enter the page number: ")) + for page_num, text in json_book.items(): + self.engine.save_to_file(text, os.path.join(book_name, book_name + "_page_" + (str(page_num+1) + ".mp3"))) + self.engine.runAndWait() + + + def read_book(self, input_file_path, password=None): + """ method to read the book """ + self.file_check(input_file_path) + logging.info("Creating your audiobook... Please wait...") + json_book, pages = self.create_json_book(input_file_path, password) + speak_text(self.engine, f"The book has total {str(pages)} pages!") + speak_text(self.engine, "Please enter the page number: ", display=False) + start_page = int(input("Please enter the page number: ")) - 1 + + reading = True + while reading: + if start_page > pages or start_page < 0: + speak_text(self.engine, "Invalid page number!") + speak_text(self.engine, f"The book has total {str(pages)} pages!") + start_page = int(input("Please enter the page number: ")) - speak_text(self.engine, f"Reading page {str(start_page+1)}") - pageObj = pdfReader.getPage(start_page) - pageText = pageObj.extractText() - speak_text(self.engine, pageText) + speak_text(self.engine, f"Reading page {str(start_page+1)}") + pageText = json_book[start_page] + speak_text(self.engine, pageText, display=False) + user_input = input("Please Select an option: \n 1. Type 'r' to read again: \n 2. Type 'p' to read previous page\n 3. Type 'n' to read next page\n 4. Type 'q' to quit:\n 5. Type page number to read that page:\n") + if user_input == "r": + speak_text(self.engine, f"Reading page {str(start_page+1)}") + continue + elif user_input == "p": + speak_text(self.engine, "Reading previous page") + start_page -= 1 + continue + elif user_input == "n": + speak_text(self.engine, "Reading next page") + start_page += 1 + continue + elif user_input == "q": + speak_text(self.engine, "Quitting the book!") + break + elif user_input.isnumeric(): + start_page = int(user_input) - 1 + else: user_input = input("Please Select an option: \n 1. Type 'r' to read again: \n 2. Type 'p' to read previous page\n 3. Type 'n' to read next page\n 4. Type 'q' to quit:\n 5. Type page number to read that page:\n") - if user_input == "r": - speak_text(self.engine, f"Reading page {str(start_page+1)}") - continue - elif user_input == "p": - speak_text(self.engine, "Reading previous page") - start_page -= 1 - continue - elif user_input == "n": - speak_text(self.engine, "Reading next page") - start_page += 1 - continue - elif user_input == "q": - speak_text(self.engine, "Quitting the book!") - break - elif user_input.isnumeric(): - start_page = int(user_input) - 1 - else: - user_input = input("Please Select an option: \n 1. Type 'r' to read again: \n 2. Type 'p' to read previous page\n 3. Type 'n' to read next page\n 4. Type 'q' to quit:\n 5. Type page number to read that page:\n") - continue + continue diff --git a/setup.py b/setup.py index b5c4986..350fba5 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="audiobook", - version="1.0.4", + version="2.0.0", author="CodePerfectPlus", author_email="deepak008@live.com", description="Listen to your favourite audiobook",