Skip to content

Commit 9587636

Browse files
committed
released GUI version
1 parent 3f6139e commit 9587636

File tree

4 files changed

+145
-9
lines changed

4 files changed

+145
-9
lines changed

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# royalroad-fetchAndConvert
2-
Automatically download a royalroad story, convert it to kindle format and push it to the device.
2+
Automatically download a royalroad story, optionally convert it to kindle format and push it to the device.
3+
4+
![GUI Homepage](homepage.png)
35

46

57
# Features
@@ -11,6 +13,19 @@ Automatically download a royalroad story, convert it to kindle format and push i
1113
* Support CSS rules for different stories (e.g. "Everybody Love Large Chest")
1214
* Convert the story in the azw3 format, and push it to the kindle.
1315

16+
17+
# GUI
18+
19+
Download the released graphical version. For now, only the downloading is supported. The ebook generated need to be given to Calibre to convert it & to push it to your device.
20+
21+
## Developpement
22+
23+
The GUI has been developped with Tkinter. Specifically, [customtkinter](https://github.com/tomschimansky/customtkinter). All the front end is inside the file ``gui_rfetcher.py``.
24+
25+
The release is made by following this [official](https://customtkinter.tomschimansky.com/documentation/packaging) tutorial.
26+
27+
# CLI
28+
1429
# Install
1530

1631
Install Python3 on your operating system.

fetch_book.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88

99

10-
def main(next_url,nb_chapters,file_name,writeFile=True):
10+
def main(next_url,nb_chapters,file_name,writeFile=True,logger=print):
1111

1212
CSS='''<style>
1313
.chapter-content table {
@@ -25,9 +25,9 @@ def main(next_url,nb_chapters,file_name,writeFile=True):
2525
</style>
2626
'''
2727

28-
print("URL : ",next_url)
29-
print("Number of chapters : ",nb_chapters)
30-
print("File name : ",file_name,"\n")
28+
logger("URL : ",next_url)
29+
logger("Number of chapters : ",nb_chapters)
30+
logger("File name : ",file_name,"\n")
3131

3232
session = HTMLSession()
3333
if writeFile:
@@ -51,7 +51,7 @@ def main(next_url,nb_chapters,file_name,writeFile=True):
5151
chapter_title = (s.html.find('h1.font-white')[0]).text
5252
S+='<h1 class=\"chapter\">' + chapter_title + '</h1>\n'
5353

54-
print(i," ",chapter_title)
54+
logger(i," ",chapter_title)
5555

5656
#Fetch the chapter content
5757
chapter_content = s.html.find('.chapter-inner',first=True).html
@@ -81,11 +81,11 @@ def main(next_url,nb_chapters,file_name,writeFile=True):
8181
#Fetch the url of the next chapter
8282
next_url = "https://www.royalroad.com" + (s.html.find('[rel=next]')[0]).attrs.get("href")
8383
except:
84-
print("Last chapter ! Exiting...")
84+
logger("Last chapter ! Exiting...")
8585
break
8686

8787
except:
88-
print("Error on chapter",i,sys.exc_info()[0])
88+
logger("Error on chapter",i,sys.exc_info()[0])
8989

9090
if writeFile:
9191
f.close()
@@ -99,7 +99,7 @@ def main(next_url,nb_chapters,file_name,writeFile=True):
9999

100100

101101
def printUsage():
102-
print("Usage :\n fetch_book.py url_of_chapter number_of_chapters name_of_ebook")
102+
logger("Usage :\n fetch_book.py url_of_chapter number_of_chapters name_of_ebook")
103103

104104

105105
if __name__ == "__main__":

gui_rfetcher.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import customtkinter
2+
import fetch_book
3+
from urllib.parse import urlparse
4+
import threading
5+
6+
class App(customtkinter.CTk):
7+
def __init__(self):
8+
super().__init__()
9+
10+
self.title("RFetcher")
11+
self.geometry("600x380")
12+
self.grid_columnconfigure((0,1), weight=1)
13+
self.grid_rowconfigure(0, weight=1)
14+
15+
# FRAME 1 (LEFT PART)
16+
self.checkbox_frame = customtkinter.CTkScrollableFrame(self)
17+
self.checkbox_frame.grid(row=0, column=0, padx=10, pady=(10, 0), sticky="nsew")
18+
19+
# URL Input
20+
self.label_chapter = customtkinter.CTkLabel(self.checkbox_frame, text="Chapter URL", fg_color="transparent")
21+
self.label_chapter.grid(row=0, column=0, padx=10, pady=(10, 0), sticky="w")
22+
self.entry = customtkinter.CTkEntry(self.checkbox_frame, placeholder_text="https://royalroad.com/...")
23+
self.entry.grid(row=1, column=0, padx=10, pady=(10, 0), sticky="w")
24+
25+
26+
# Number of chapter to scan INPUT
27+
self.label_chapter_number = customtkinter.CTkLabel(self.checkbox_frame, text="Number of chapter to scan", fg_color="transparent")
28+
self.label_chapter_number.grid(row=2, column=0, padx=10, pady=(10, 0), sticky="w")
29+
self.entry_number = customtkinter.CTkEntry(self.checkbox_frame, placeholder_text="20")
30+
self.entry_number.grid(row=3, column=0, padx=10, pady=(10, 0), sticky="w")
31+
32+
33+
# Name of your ebook INPUT
34+
self.label_chapter_name = customtkinter.CTkLabel(self.checkbox_frame, text="Name of your ebook (no space)", fg_color="transparent")
35+
self.label_chapter_name.grid(row=4, column=0, padx=10, pady=(10, 0), sticky="w")
36+
self.entry_name = customtkinter.CTkEntry(self.checkbox_frame, placeholder_text="Mother_Of_Learning")
37+
self.entry_name.grid(row=5, column=0, padx=10, pady=(10, 0), sticky="w")
38+
39+
40+
# # Convert CHECKBOX
41+
# self.checkbox_convert = customtkinter.CTkCheckBox(self.checkbox_frame, text="Convert to AZW3")
42+
# self.checkbox_convert.grid(row=6, column=0, padx=10, pady=(10, 0), sticky="w")
43+
44+
# # Push to device CHECKBOX
45+
# self.checkbox_convert = customtkinter.CTkCheckBox(self.checkbox_frame, text="Push to your kindle")
46+
# self.checkbox_convert.grid(row=7, column=0, padx=10, pady=(10, 0), sticky="w")
47+
48+
49+
# FRAME 2 (RIGHT PART)
50+
51+
self.textbox = customtkinter.CTkTextbox(master=self, corner_radius=0)
52+
self.textbox.grid(row=0, column=1, sticky="nsew")
53+
self.textbox.configure(state="disabled")
54+
55+
# FETCH Button
56+
self.button = customtkinter.CTkButton(self, text="GO !", command=self.fetch_callback)
57+
self.button.grid(row=1, column=0, padx=10, pady=10, sticky="ew", columnspan=2)
58+
59+
60+
61+
62+
def fetch_callback(self):
63+
print(f"Fetch button pressed! {self.entry.get()} {self.entry_number.get()} {self.entry_name.get()}")
64+
65+
chapter_url = self.entry.get()
66+
number_of_chapters = self.entry_number.get()
67+
file_name = self.entry_name.get().strip().replace(" ","")
68+
69+
70+
# CHECK URL
71+
invalid_url = False
72+
if "https://www.royalroad.com" not in chapter_url:
73+
invalid_url = True
74+
try:
75+
result = urlparse(chapter_url)
76+
if all([result.scheme, result.netloc]) == False:
77+
invalid_url = True
78+
except AttributeError:
79+
invalid_url = True
80+
if invalid_url:
81+
self.append_log("Please enter a valid Chapter URL : https://www.royalroad.com/fiction/....")
82+
return False
83+
84+
# CHECK FIELD LENGTH
85+
if len(chapter_url) == 0 or len(number_of_chapters) == 0 or len(file_name) == 0:
86+
self.append_log("Please verify input, some fields are empty")
87+
return False
88+
89+
# CHECK FIELD FORMAT
90+
try:
91+
int(number_of_chapters)
92+
except:
93+
self.append_log("Number of chapter should be an integer and not a string")
94+
return False
95+
96+
97+
# fetch_book.main(chapter_url,int(number_of_chapters),"{0}.html".format(file_name),logger=self.append_log)
98+
99+
self.button.configure(state="disabled")
100+
threading.Thread(target=self.fetch_book, args=(chapter_url, int(number_of_chapters), f"{file_name}.html")).start()
101+
102+
def fetch_book(self, chapter_url, number_of_chapters, file_name):
103+
fetch_book.main(chapter_url, number_of_chapters, file_name, logger=self.append_log)
104+
self.button.configure(state="normal")
105+
self.textbox.see("end")
106+
self.append_log("Find your ebook inside the RFetcher folder !")
107+
108+
def append_log(self, *args):
109+
110+
# I use *args instead of a simple variable to be able to cactch several args, in a print-like fashion
111+
log_input = " ".join(map(str, args))
112+
113+
self.textbox.configure(state="normal")
114+
self.textbox.insert("end", log_input + "\n")
115+
self.textbox.configure(state="disabled")
116+
117+
118+
119+
app = App()
120+
121+
app.mainloop()

homepage.png

27.6 KB
Loading

0 commit comments

Comments
 (0)