Skip to content

Commit 666ced8

Browse files
authored
Multiple ebook formats & ebook_name config (#20)
* Bump version: 0.8.1 → 0.8.2 Added the feature to download multiple ebook formats simultaneously * fix: Plugins not being added * fix: URL encoding issue * fix: File hash issue Refactored the code in `fichub.py` to save the hash & download_url for the fic correctly * chore: Added `meta` props to the files dictionary * Bump version: 0.8.2 → 0.8.3 Added `filename_format` feature where users can configure the file name for the book as per the props mentioned in the README * refactor: Exception Handler Changed the Exception Handler from catching only Attribute Errors to all Errors * fix: Remove extraMeta prop from the filename_format options * fix: Rename meta key's id to ichub_id to avoid conflict * fix: Hardcoded filename_formats to ensure backwards compatibility with future API updates * fix: Remove unsafe chars from the filename * Bump version: 0.8.3 → 0.8.4 Added priority based processing for the rawExtendedMeta & extraMeta where it will process it in this order: rawExtendedMeta >> extraMeta >> None * refactor: extraMeta processing * Bump version: 0.8.4 → 0.9.0
1 parent f6c183a commit 666ced8

File tree

10 files changed

+268
-156
lines changed

10 files changed

+268
-156
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 0.8.1
2+
current_version = 0.9.0
33
commit = True
44
tag = False
55
parse = ^

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ __pycache__/
55

66
# ebooks
77
*.epub
8+
*.mobi
9+
*.pdf
810

911
# C extensions
1012
*.so

README.md

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ Options:
4545
-v, --verbose Show fic stats
4646
-o, --out-dir TEXT Path to the Output directory for files (default:
4747
Current Directory)
48-
--format TEXT Download Format: epub (default), mobi, pdf or html
49-
[default: epub]
48+
--format TEXT Download Formats, comma separated if multiple: epub (default), mobi, pdf or html
5049
--force Force overwrite of an existing file
5150
-ss, --supported-sites List of supported sites
5251
-d, --debug Show the log in the console for debugging
@@ -60,7 +59,7 @@ Options:
6059

6160
# Default Configuration
6261

63-
- The fanfiction will be downloaded in epub format. To change it, use `-f` followed by the format.
62+
- The fanfiction will be downloaded in epub format. To change it, use `--format` followed by the format. Multiple formats can be selected by separating them by commas.
6463
- The fanfiction will be downloaded in the current directory. To change it, use `-o` followed by the path to the directory.
6564
- Failed downloads will be saved in the `err.log` file in the current directory.
6665

@@ -86,6 +85,12 @@ fichub_cli -i urls.txt
8685
fichub_cli -l "https://www.fanfiction.net/s/11191235/1/Harry-Potter-and-the-Prince-of-Slytherin,https://www.fanfiction.net/s/13720575/1/A-Cadmean-Victory-Remastered"
8786
```
8887

88+
- To download multiple formats
89+
90+
```
91+
fichub_cli -u "https://www.fanfiction.net/s/13720575/1/A-Cadmean-Victory-Remastered" --format epub,mobi
92+
```
93+
8994
- To generate a changelog of the download
9095

9196
```
@@ -106,7 +111,17 @@ fichub_cli -i urls.txt --changelog
106111

107112
# Configuration
108113

109-
- Users can configure centain things like `db_up_time_format`, `fic_up_time_format` & `delete_output_log` etc by editing the `config.json` file in the app directory.
114+
- Users can configure centain things like `db_up_time_format`, `fic_up_time_format`, `delete_output_log` & `filename_format` by editing the `config.json` file in the app directory.
115+
116+
- Filename format props (case-sensitive):
117+
`author, fichubAuthorId, authorId, chapters, created, fichubId, genres, id, language, rated, fandom, status, updated, title`
118+
119+
Example:
120+
121+
```
122+
"filename_format": "[title] by [author]"
123+
```
124+
110125
- To locate the config file, run `fichub_cli --config-info` and open the `config.json` file in an editor and make the necessary changes.
111126

112127
## Notes

fichub_cli/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.8.1"
1+
__version__ = "0.9.0"

fichub_cli/cli.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,15 @@
3434

3535
app = typer.Typer(add_completion=False)
3636
app_dirs = PlatformDirs("fichub_cli", "fichub")
37-
3837
discovered_plugins = {
3938
name: importlib.import_module(name)
4039
for finder, name, ispkg
4140
in pkgutil.iter_modules()
4241
if name.startswith('fichub_cli_')
4342
}
44-
4543
for plugin in discovered_plugins.values():
46-
app.add_typer(plugin.app)
44+
if not plugin.__name__.endswith("-script"):
45+
app.add_typer(plugin.app)
4746

4847
# build/update the app directory & the config file
4948
appdir_builder(app_dirs)
@@ -72,7 +71,7 @@ def default(
7271
"", "-o", " --out-dir", help="Path to the Output directory for files (default: Current Directory)"),
7372

7473
format: str = typer.Option(
75-
"epub", help="Download Format: epub (default), mobi, pdf or html"),
74+
"epub", help="Download Formats, comma separated if multiple: epub (default), mobi, pdf or html"),
7675

7776
force: bool = typer.Option(
7877
False, "--force", help="Force overwrite of an existing file", is_flag=True),
@@ -191,9 +190,7 @@ def default(
191190
if fic.exit_status == 1:
192191
typer.echo(
193192
Fore.RED +
194-
"\nThe CLI ran into some errors! Check " + Style.RESET_ALL +
195-
Fore.YELLOW + "err.log" + Style.RESET_ALL + Fore.RED +
196-
" in the current directory!" + Style.RESET_ALL)
193+
"\nThe CLI ran into some errors! Check the console for the log messages!" + Style.RESET_ALL)
197194

198195
if os.path.exists("output.log"):
199196
with open(os.path.join(app_dirs.user_data_dir, "config.json"), 'r') as f:

fichub_cli/utils/fetch_data.py

Lines changed: 61 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,19 @@
1616
from tqdm import tqdm
1717
from colorama import Fore
1818
from loguru import logger
19+
import traceback
1920

2021
from .fichub import FicHub
2122
from .logging import init_log, download_processing_log, \
2223
verbose_log
2324
from .processing import check_url, save_data, \
24-
urls_preprocessing, check_output_log, build_changelog
25+
urls_preprocessing, build_changelog
2526

2627
bar_format = "{l_bar}{bar}| {n_fmt}/{total_fmt}, {rate_fmt}{postfix}, ETA: {remaining}"
2728

2829

2930
class FetchData:
30-
def __init__(self, format_type=0, out_dir="", force=False,
31+
def __init__(self, format_type=[0], out_dir="", force=False,
3132
debug=False, changelog=False, automated=False, verbose=False):
3233
self.format_type = format_type
3334
self.out_dir = out_dir
@@ -84,14 +85,14 @@ def get_fic_with_infile(self, infile: str):
8485
# update the exit status
8586
self.exit_status = fic.exit_status
8687

87-
if fic.file_name is None:
88+
if not fic.files:
8889
self.exit_status = 1
8990

9091
else:
9192
self.exit_status, url_exit_status = save_data(
92-
self.out_dir, fic.file_name,
93-
fic.download_url, self.debug, self.force,
94-
fic.cache_hash, self.exit_status,
93+
self.out_dir, fic.files,
94+
self.debug, self.force,
95+
self.exit_status,
9596
self.automated)
9697

9798
with open("output.log", "a") as file:
@@ -103,9 +104,11 @@ def get_fic_with_infile(self, infile: str):
103104
no_updates_urls.append(url)
104105
pbar.update(1)
105106

106-
# Error: 'FicHub' object has no attribute 'file_name'
107+
# Error: 'FicHub' object has no attribute 'files'
107108
# Reason: Unsupported URL
108-
except AttributeError:
109+
except Exception as e:
110+
if self.debug:
111+
logger.error(str(traceback.format_exc()))
109112
with open("err.log", "a") as file:
110113
file.write(url.strip()+"\n")
111114
err_urls.append(url)
@@ -165,14 +168,14 @@ def get_fic_with_list(self, list_url: str):
165168
# update the exit status
166169
self.exit_status = fic.exit_status
167170

168-
if fic.file_name is None:
171+
if not fic.files:
169172
self.exit_status = 1
170173

171174
else:
172175
self.exit_status, url_exit_status = save_data(
173-
self.out_dir, fic.file_name,
174-
fic.download_url, self.debug, self.force,
175-
fic.cache_hash, self.exit_status, self.automated)
176+
self.out_dir, fic.files,
177+
self.debug, self.force,
178+
self.exit_status, self.automated)
176179

177180
with open("output.log", "a") as file:
178181
file.write(f"{url}\n")
@@ -184,9 +187,11 @@ def get_fic_with_list(self, list_url: str):
184187

185188
pbar.update(1)
186189

187-
# Error: 'FicHub' object has no attribute 'file_name'
190+
# Error: 'FicHub' object has no attribute 'files'
188191
# Reason: Unsupported URL
189-
except AttributeError:
192+
except Exception as e:
193+
if self.debug:
194+
logger.error(str(traceback.format_exc()))
190195
with open("err.log", "a") as file:
191196
file.write(url.strip()+"\n")
192197
err_urls.append(url)
@@ -217,54 +222,56 @@ def get_fic_with_url(self, url_input: str):
217222
if self.debug:
218223
logger.info("-u flag used!")
219224

220-
url = check_output_log([url_input], self.debug)
221-
225+
url, _ = urls_preprocessing([url_input], self.debug)
222226
if url:
223-
init_log(self.debug, self.force)
224-
with tqdm(total=1, ascii=False,
225-
unit="file", bar_format=bar_format) as pbar:
227+
if url[0]:
228+
init_log(self.debug, self.force)
229+
with tqdm(total=1, ascii=False,
230+
unit="file", bar_format=bar_format) as pbar:
226231

227-
download_processing_log(self.debug, url[0])
228-
supported_url, self.exit_status = check_url(
229-
url[0], self.debug, self.exit_status)
232+
download_processing_log(self.debug, url[0])
233+
supported_url, self.exit_status = check_url(
234+
url[0], self.debug, self.exit_status)
230235

231-
if supported_url:
232-
try:
233-
fic = FicHub(self.debug, self.automated,
234-
self.exit_status)
235-
fic.get_fic_metadata(url[0], self.format_type)
236+
if supported_url:
237+
try:
238+
fic = FicHub(self.debug, self.automated,
239+
self.exit_status)
240+
fic.get_fic_metadata(url[0], self.format_type)
236241

237-
if self.verbose:
238-
verbose_log(self.debug, fic)
242+
if self.verbose:
243+
verbose_log(self.debug, fic)
239244

240-
# update the exit status
241-
self.exit_status = fic.exit_status
245+
# update the exit status
246+
self.exit_status = fic.exit_status
242247

243-
if fic.file_name is None:
244-
self.exit_status = 1
248+
if not fic.files:
249+
self.exit_status = 1
245250

246-
else:
247-
self.exit_status, _ = save_data(
248-
self.out_dir, fic.file_name,
249-
fic.download_url, self.debug, self.force,
250-
fic.cache_hash, self.exit_status, self.automated)
251-
with open("output.log", "a") as file:
252-
file.write(f"{url[0]}\n")
253-
pbar.update(1)
251+
else:
252+
self.exit_status, _ = save_data(
253+
self.out_dir, fic.files,
254+
self.debug, self.force,
255+
self.exit_status, self.automated)
256+
with open("output.log", "a") as file:
257+
file.write(f"{url[0]}\n")
258+
pbar.update(1)
254259

255-
# Error: 'FicHub' object has no attribute 'file_name'
256-
# Reason: Unsupported URL
257-
except AttributeError:
260+
# Error: 'FicHub' object has no attribute 'files'
261+
# Reason: Unsupported URL
262+
except Exception:
263+
if self.debug:
264+
logger.error(str(traceback.format_exc()))
265+
with open("err.log", "a") as file:
266+
file.write(url[0].strip()+"\n")
267+
pbar.update(1)
268+
self.exit_status = 1
269+
pass # skip the unsupported url
270+
271+
else: # skip the unsupported url
258272
with open("err.log", "a") as file:
259273
file.write(url[0].strip()+"\n")
260274
pbar.update(1)
261-
self.exit_status = 1
262-
pass # skip the unsupported url
263-
264-
else: # skip the unsupported url
265-
with open("err.log", "a") as file:
266-
file.write(url[0].strip()+"\n")
267-
pbar.update(1)
268-
else:
269-
typer.echo(Fore.RED +
270-
"No new urls found! If output.log exists, please clear it.")
275+
else:
276+
typer.echo(Fore.RED +
277+
"No new urls found! If output.log exists, please clear it.")

fichub_cli/utils/fichub.py

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def __init__(self, debug, automated, exit_status):
4444
self.http.mount("https://", adapter)
4545
self.http.mount("http://", adapter)
4646

47-
def get_fic_metadata(self, url: str, format_type: int):
47+
def get_fic_metadata(self, url: str, format_type: list):
4848
"""
4949
Sends GET request to Fichub API to fetch the metadata
5050
"""
@@ -70,44 +70,47 @@ def get_fic_metadata(self, url: str, format_type: int):
7070
break
7171
except (ConnectionError, TimeoutError, Exception) as e:
7272
if self.debug:
73-
logger.error(str(e))
73+
logger.error(str(traceback.format_exc()))
7474
tqdm.write("\n" + Fore.RED + str(e) + Style.RESET_ALL +
7575
Fore.GREEN + "\nWill retry in 3s!" +
7676
Style.RESET_ALL)
7777
time.sleep(3)
7878

7979
try:
8080
self.response = response.json()
81-
82-
if format_type == 0:
83-
cache_url = self.response['epub_url']
84-
self.cache_hash = (
85-
re.search(r"\?h=(.*)", self.response['epub_url'])).group(1)
86-
self.file_format = ".epub"
87-
88-
elif format_type == 1:
89-
cache_url = self.response['mobi_url']
90-
self.cache_hash = (
91-
re.search(r"\?h=(.*)", self.response['epub_url'])).group(1)
92-
self.file_format = ".mobi"
93-
94-
elif format_type == 2:
95-
cache_url = self.response['pdf_url']
96-
self.cache_hash = (
97-
re.search(r"\?h=(.*)", self.response['epub_url'])).group(1)
98-
self.file_format = ".pdf"
99-
100-
elif format_type == 3:
101-
cache_url = self.response['html_url']
102-
self.cache_hash = (
103-
re.search(r"\?h=(.*)", self.response['epub_url'])).group(1)
104-
self.file_format = ".zip"
105-
106-
self.file_name = self.response['epub_url'].split(
107-
"/")[4].split("?")[0]
108-
self.file_name = self.file_name.replace(".epub", self.file_format)
109-
self.download_url = "https://fichub.net"+cache_url
110-
81+
self.file_format =[]
82+
self.cache_hash = {}
83+
cache_urls= {}
84+
85+
for format in format_type:
86+
if format == 0:
87+
cache_urls['epub'] = self.response['urls']['epub']
88+
self.cache_hash['epub'] = self.response['hashes']['epub']
89+
self.file_format.append(".epub")
90+
91+
elif format == 1:
92+
cache_urls['mobi'] = self.response['urls']['mobi']
93+
self.cache_hash['mobi'] = self.response['hashes']['epub']
94+
self.file_format.append(".mobi")
95+
96+
elif format == 2:
97+
cache_urls['pdf'] = self.response['urls']['pdf']
98+
self.cache_hash['pdf'] = self.response['hashes']['epub']
99+
self.file_format.append(".pdf")
100+
101+
elif format == 3:
102+
cache_urls['zip'] =self.response['urls']['html']
103+
self.cache_hash['zip'] = self.response['hashes']['epub']
104+
self.file_format.append(".zip")
105+
106+
self.files = {}
107+
self.files["meta"] = self.response['meta']
108+
for file_format in self.file_format:
109+
self.files[self.response['urls']['epub'].split(
110+
"/")[4].split("?")[0].replace(".epub", file_format)] = {
111+
"hash": self.cache_hash[file_format.replace(".","")],
112+
"download_url": "https://fichub.net"+cache_urls[file_format.replace(".","")]
113+
}
111114
# Error: 'epub_url'
112115
# Reason: Unsupported URL
113116
except (KeyError, UnboundLocalError) as e:
@@ -144,7 +147,7 @@ def get_fic_data(self, download_url: str):
144147
break
145148
except (ConnectionError, TimeoutError, Exception) as e:
146149
if self.debug:
147-
logger.error(str(e))
150+
logger.error(str(traceback.format_exc()))
148151
tqdm.write("\n" + Fore.RED + str(e) + Style.RESET_ALL +
149152
Fore.GREEN + "\nWill retry in 3s!" +
150153
Style.RESET_ALL)

0 commit comments

Comments
 (0)