web-novel-scraper 1.1.0__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,37 +1,32 @@
1
- import json
2
1
  from pathlib import Path
3
- import sys
4
2
  from datetime import datetime
3
+ from typing import Optional
5
4
 
6
5
  import click
7
6
 
8
- from .file_manager import FileManager
7
+ from .config_manager import ScraperConfig
9
8
  from .novel_scraper import Novel
10
9
  from .version import __version__
11
10
 
12
11
  CURRENT_DIR = Path(__file__).resolve().parent
13
12
 
14
- def obtain_novel(novel_title: str, novel_base_dir: str = None, allow_not_exists: bool = False) -> Novel:
15
- """Obtain a novel instance from the file system."""
16
- file_manager = FileManager(
17
- novel_title=novel_title, novel_base_dir=novel_base_dir, read_only=True)
18
- novel_json = file_manager.load_novel_json()
19
- if novel_json:
20
- try:
21
- novel = Novel.from_json(novel_json)
22
- return novel
23
- except KeyError:
24
- click.echo(
25
- 'JSON file seems to be manipulated, please check it.', err=True)
26
- except json.decoder.JSONDecodeError:
27
- click.echo(
28
- 'JSON file seems to be corrupted, please check it.', err=True)
29
- elif allow_not_exists:
30
- return None
31
- else:
32
- click.echo(
33
- 'Novel with that title does not exist or the main data file was deleted.', err=True)
34
- sys.exit(1)
13
+ def global_options(f):
14
+ f = click.option('-nb', '--novel-base-dir', type=click.Path(), required=False, help="Alternative directory for this novel.")(f)
15
+ f = click.option('--config-file', type=click.Path(), required=False, help="Path to config file.")(f)
16
+ f = click.option('--base-novels-dir', type=click.Path(), required=False, help="Alternative base directory for all novels.")(f)
17
+ f = click.option('--decode-guide-file', type=click.Path(), required=False, help="Path to alternative decode guide file.")(f)
18
+ return f
19
+
20
+ def obtain_novel(title, ctx_opts, allow_missing=False):
21
+ cfg = ScraperConfig(ctx_opts.get("CONFIG_FILE"), ctx_opts.get("BASE_NOVELS_DIR"))
22
+ try:
23
+ return Novel.load(title, cfg, ctx_opts.get("NOVEL_BASE_DIR"))
24
+ except ValueError:
25
+ if allow_missing:
26
+ return None
27
+ click.echo("Novel not found.", err=True)
28
+ exit(1)
29
+
35
30
 
36
31
  def validate_date(ctx, param, value):
37
32
  """Validate the date format."""
@@ -57,8 +52,15 @@ novel_base_dir_option = click.option(
57
52
  '-nb', '--novel-base-dir', type=str, help='Alternative base directory for the novel files.')
58
53
 
59
54
  @click.group()
60
- def cli():
55
+ @global_options
56
+ @click.pass_context
57
+ def cli(ctx, novel_base_dir, config_file, base_novels_dir, decode_guide_file):
61
58
  """CLI Tool for web novel scraping."""
59
+ ctx.ensure_object(dict)
60
+ ctx.obj['NOVEL_BASE_DIR'] = novel_base_dir
61
+ ctx.obj['CONFIG_FILE'] = config_file
62
+ ctx.obj['BASE_NOVELS_DIR'] = base_novels_dir
63
+ ctx.obj['DECODE_GUIDE_FILE'] = decode_guide_file
62
64
 
63
65
  # Metadata:
64
66
  metadata_author_option = click.option(
@@ -100,8 +102,8 @@ force_flaresolver_option = click.option('--force-flaresolver', is_flag=True, sho
100
102
  # Novel creation and data management commands
101
103
 
102
104
  @cli.command()
105
+ @click.pass_context
103
106
  @title_option
104
- @novel_base_dir_option
105
107
  @toc_main_url_option
106
108
  @create_toc_html_option()
107
109
  @host_option
@@ -115,9 +117,9 @@ force_flaresolver_option = click.option('--force-flaresolver', is_flag=True, sho
115
117
  @save_title_to_content_option
116
118
  @auto_add_host_option
117
119
  @force_flaresolver_option
118
- def create_novel(title, novel_base_dir, toc_main_url, toc_html, host, author, start_date, end_date, language, description, tags, cover, save_title_to_content, auto_add_host, force_flaresolver):
120
+ def create_novel(ctx, title, toc_main_url, toc_html, host, author, start_date, end_date, language, description, tags, cover, save_title_to_content, auto_add_host, force_flaresolver):
119
121
  """Creates a new novel and saves it."""
120
- novel = obtain_novel(title, novel_base_dir, allow_not_exists=True)
122
+ novel = obtain_novel(title, ctx.obj, allow_missing=True)
121
123
  if novel:
122
124
  click.confirm(f'A novel with the title {title} already exists, do you want to replace it?', abort=True)
123
125
  novel.delete_toc()
@@ -138,9 +140,16 @@ def create_novel(title, novel_base_dir, toc_main_url, toc_html, host, author, st
138
140
  toc_html_content = None
139
141
  if toc_html:
140
142
  toc_html_content = toc_html.read()
141
-
142
- novel = Novel(title, toc_main_url=toc_main_url,
143
- toc_html=toc_html_content, host=host, novel_base_dir=novel_base_dir)
143
+ novel = Novel(title=title,
144
+ toc_main_url=toc_main_url,
145
+ toc_html=toc_html_content,
146
+ host=host
147
+ )
148
+ novel.set_config(config_file=ctx.obj.get('CONFIG_FILE'),
149
+ base_novels_dir=ctx.obj.get('BASE_NOVELS_DIR'),
150
+ novel_base_dir=ctx.obj.get('NOVEL_BASE_DIR'),
151
+ decode_guide_file=ctx.obj.get('DECODE_GUIDE_FILE')
152
+ )
144
153
  novel.set_metadata(author=author, start_date=start_date,
145
154
  end_date=end_date, language=language, description=description)
146
155
  novel.set_scraper_behavior(save_title_to_content=save_title_to_content,
@@ -151,189 +160,199 @@ def create_novel(title, novel_base_dir, toc_main_url, toc_html, host, author, st
151
160
  if cover:
152
161
  if not novel.set_cover_image(cover):
153
162
  click.echo('Error saving the novel cover image.', err=True)
163
+ novel.save_novel()
154
164
  click.echo('Novel saved successfully.')
155
165
 
156
166
  @cli.command()
167
+ @click.pass_context
157
168
  @title_option
158
- @novel_base_dir_option
159
- def show_novel_info(title, novel_base_dir):
169
+ def show_novel_info(ctx, title):
160
170
  """Show information about a novel."""
161
- novel = obtain_novel(title, novel_base_dir)
171
+ novel = obtain_novel(title, ctx.obj)
162
172
  click.echo(novel)
163
173
 
164
174
  @cli.command()
175
+ @click.pass_context
165
176
  @title_option
166
- @novel_base_dir_option
167
177
  @metadata_author_option
168
178
  @metadata_start_date_option
169
179
  @metadata_end_date_option
170
180
  @metadata_language_option
171
181
  @metadata_description_option
172
- def set_metadata(title, novel_base_dir, author, start_date, end_date, language, description):
182
+ def set_metadata(ctx, title, author, start_date, end_date, language, description):
173
183
  """Set metadata for a novel."""
174
- novel = obtain_novel(title, novel_base_dir)
184
+ novel = obtain_novel(title, ctx.obj)
175
185
  novel.set_metadata(author=author, start_date=start_date,
176
186
  end_date=end_date, language=language, description=description)
187
+ novel.save_novel()
177
188
  click.echo('Novel metadata saved successfully.')
178
189
  click.echo(novel.metadata)
179
190
 
180
191
  @cli.command()
192
+ @click.pass_context
181
193
  @title_option
182
- @novel_base_dir_option
183
- def show_metadata(title, novel_base_dir):
194
+ def show_metadata(ctx, title):
184
195
  """Show metadata of a novel."""
185
- novel = obtain_novel(title, novel_base_dir)
196
+ novel = obtain_novel(title, ctx.obj)
186
197
  click.echo(novel.metadata)
187
198
 
188
199
  @cli.command()
200
+ @click.pass_context
189
201
  @title_option
190
- @novel_base_dir_option
191
202
  @click.option('--tag', 'tags', type=str, help='Tag to be added', multiple=True)
192
- def add_tags(title, novel_base_dir, tags):
203
+ def add_tags(ctx, title, tags):
193
204
  """Add tags to a novel."""
194
- novel = obtain_novel(title, novel_base_dir)
205
+ novel = obtain_novel(title, ctx.obj)
195
206
  for tag in tags:
196
207
  if not novel.add_tag(tag):
197
208
  click.echo(f'Tag {tag} already exists', err=True)
209
+ novel.save_novel()
198
210
  click.echo(f'Tags: {", ".join(novel.metadata.tags)}')
199
211
 
200
212
  @cli.command()
213
+ @click.pass_context
201
214
  @title_option
202
- @novel_base_dir_option
203
215
  @click.option('--tag', 'tags', type=str, help='Tag to be removed.', multiple=True)
204
- def remove_tags(title, novel_base_dir, tags):
216
+ def remove_tags(ctx, title, tags):
205
217
  """Remove tags from a novel."""
206
- novel = obtain_novel(title, novel_base_dir)
218
+ novel = obtain_novel(title, ctx.obj)
207
219
  for tag in tags:
208
220
  if not novel.remove_tag(tag):
209
221
  click.echo(f'Tag {tag} does not exist.', err=True)
222
+ novel.save_novel()
210
223
  click.echo(f'Tags: {", ".join(novel.metadata.tags)}')
211
224
 
212
225
  @cli.command()
226
+ @click.pass_context
213
227
  @title_option
214
- @novel_base_dir_option
215
- def show_tags(title, novel_base_dir):
228
+ def show_tags(ctx, title):
216
229
  """Show tags of a novel."""
217
- novel = obtain_novel(title, novel_base_dir)
230
+ novel = obtain_novel(title, ctx.obj)
218
231
  click.echo(f'Tags: {", ".join(novel.metadata.tags)}')
219
232
 
220
233
  @cli.command()
234
+ @click.pass_context
221
235
  @title_option
222
- @novel_base_dir_option
223
236
  @click.option('--cover-image', type=str, required=True, help='Filepath of the cover image.')
224
- def set_cover_image(title, novel_base_dir, cover_image):
237
+ def set_cover_image(ctx, title, cover_image):
225
238
  """Set the cover image for a novel."""
226
- novel = obtain_novel(title, novel_base_dir)
239
+ novel = obtain_novel(title, ctx.obj)
227
240
  if not novel.set_cover_image(cover_image):
228
241
  click.echo('Error saving the cover image.', err=True)
229
242
  else:
230
243
  click.echo('New cover image set successfully.')
231
244
 
232
245
  @cli.command()
246
+ @click.pass_context
233
247
  @title_option
234
- @novel_base_dir_option
235
248
  @click.option('--save-title-to-content', type=bool, help='Toggle the title of the chapter being added to the content (use true or false).')
236
249
  @click.option('--auto-add-host', type=bool, help='Toggle automatic addition of the host to chapter URLs (use true or false).')
237
250
  @click.option('--force-flaresolver', type=bool, help='Toggle forcing the use of FlareSolver (use true or false).')
238
251
  @click.option('--hard-clean', type=bool, help='Toggle using a hard clean when cleaning HTML files (use true or false).')
239
- def set_scraper_behavior(title, novel_base_dir, save_title_to_content, auto_add_host, force_flaresolver, hard_clean):
252
+ def set_scraper_behavior(ctx, title, save_title_to_content, auto_add_host, force_flaresolver, hard_clean):
240
253
  """Set scraper behavior for a novel."""
241
- novel = obtain_novel(title, novel_base_dir)
254
+ novel = obtain_novel(title, ctx.obj)
242
255
  novel.set_scraper_behavior(
243
256
  save_title_to_content=save_title_to_content,
244
257
  auto_add_host=auto_add_host,
245
258
  force_flaresolver=force_flaresolver,
246
259
  hard_clean=hard_clean
247
260
  )
261
+ novel.save_novel()
248
262
  click.echo('New scraper behavior added successfully.')
249
263
 
250
264
  @cli.command()
265
+ @click.pass_context
251
266
  @title_option
252
- @novel_base_dir_option
253
- def show_scraper_behavior(title, novel_base_dir):
267
+ def show_scraper_behavior(ctx, title):
254
268
  """Show scraper behavior of a novel."""
255
- novel = obtain_novel(title, novel_base_dir)
269
+ novel = obtain_novel(title, ctx.obj)
256
270
  click.echo(novel.scraper_behavior)
257
271
 
258
272
  @cli.command()
273
+ @click.pass_context
259
274
  @title_option
260
- @novel_base_dir_option
261
275
  @host_option
262
- def set_host(title, novel_base_dir, host):
276
+ def set_host(ctx, title, host):
263
277
  """Set the host for a novel."""
264
- novel = obtain_novel(title, novel_base_dir)
278
+ novel = obtain_novel(title, ctx.obj)
265
279
  novel.set_host(host)
280
+ novel.save_novel()
266
281
  click.echo('New host set successfully.')
267
282
 
268
283
  # TOC MANAGEMENT COMMANDS
269
284
 
270
285
  @cli.command()
286
+ @click.pass_context
271
287
  @title_option
272
- @novel_base_dir_option
273
288
  @click.option('--toc-main-url', type=str, required=True, help='New TOC main URL (Previous links will be deleted).')
274
- def set_toc_main_url(title, novel_base_dir, toc_main_url):
289
+ def set_toc_main_url(ctx, title, toc_main_url):
275
290
  """Set the main URL for the TOC of a novel."""
276
- novel = obtain_novel(title, novel_base_dir)
291
+ novel = obtain_novel(title, ctx.obj)
277
292
  novel.set_toc_main_url(toc_main_url)
293
+ novel.save_novel()
278
294
 
279
295
  @cli.command()
296
+ @click.pass_context
280
297
  @title_option
281
- @novel_base_dir_option
282
298
  @create_toc_html_option(required=True)
283
299
  @host_option
284
- def add_toc_html(title, novel_base_dir, toc_html, host):
300
+ def add_toc_html(ctx, title, toc_html, host):
285
301
  """Add TOC HTML to a novel."""
286
- novel = obtain_novel(title, novel_base_dir)
302
+ novel = obtain_novel(title, ctx.obj)
287
303
  html_content = toc_html.read()
288
304
  novel.add_toc_html(html_content, host)
305
+ novel.save_novel()
289
306
 
290
307
  @cli.command()
308
+ @click.pass_context
291
309
  @title_option
292
- @novel_base_dir_option
293
310
  @click.option('--reload-files', is_flag=True, required=False, default=False, show_default=True, help='Reload the TOC files before sync (only works if using a TOC URL).')
294
- def sync_toc(title, novel_base_dir, reload_files):
311
+ def sync_toc(ctx, title, reload_files):
295
312
  """Sync the TOC of a novel."""
296
- novel = obtain_novel(title, novel_base_dir)
313
+ novel = obtain_novel(title, ctx.obj)
297
314
  if novel.sync_toc(reload_files):
298
315
  click.echo(
299
316
  'Table of Contents synced with files, to see the new TOC use the command show-toc.')
300
317
  else:
301
318
  click.echo(
302
319
  'Error with the TOC syncing, please check the TOC files and decoding options.', err=True)
320
+ novel.save_novel()
303
321
 
304
322
  @cli.command()
323
+ @click.pass_context
305
324
  @title_option
306
- @novel_base_dir_option
307
325
  @click.option('--auto-approve', is_flag=True, required=False, default=False, show_default=True, help='Auto approve.')
308
- def delete_toc(title, novel_base_dir, auto_approve):
326
+ def delete_toc(ctx, title, auto_approve):
309
327
  """Delete the TOC of a novel."""
310
- novel = obtain_novel(title, novel_base_dir)
328
+ novel = obtain_novel(title, ctx.obj)
311
329
  if not auto_approve:
312
330
  click.confirm(f'Are you sure you want to delete the TOC for {title}?', abort=True)
313
331
  novel.delete_toc()
332
+ novel.save_novel()
314
333
 
315
334
  @cli.command()
335
+ @click.pass_context
316
336
  @title_option
317
- @novel_base_dir_option
318
- def show_toc(title, novel_base_dir):
337
+ def show_toc(ctx, title):
319
338
  """Show the TOC of a novel."""
320
- novel = obtain_novel(title, novel_base_dir)
339
+ novel = obtain_novel(title, ctx.obj)
321
340
  click.echo(novel.show_toc())
322
341
 
323
342
  # CHAPTER MANAGEMENT COMMANDS
324
343
 
325
344
  @cli.command()
345
+ @click.pass_context
326
346
  @title_option
327
- @novel_base_dir_option
328
347
  @click.option('--chapter-url', type=str, required=False, help='Chapter URL to be scrapped.')
329
348
  @click.option('--chapter-num', type=int, required=False, help='Chapter number to be scrapped.')
330
349
  @click.option('--update-html', is_flag=True, default=False, show_default=True, help='If the chapter HTML is saved, it will be updated.')
331
- def scrap_chapter(title, novel_base_dir, chapter_url, chapter_num, update_html):
350
+ def scrap_chapter(ctx, title, chapter_url, chapter_num, update_html):
332
351
  """Scrap a chapter of a novel."""
333
352
  if (chapter_url is None and chapter_num is None) or (chapter_url and chapter_num):
334
353
  raise click.UsageError("You must set exactly one: --chapter-url o --chapter-num.")
335
354
 
336
- novel = obtain_novel(title, novel_base_dir)
355
+ novel = obtain_novel(title, ctx.obj)
337
356
 
338
357
  if chapter_num is not None:
339
358
  if chapter_num <= 0 or chapter_num > len(novel.chapters):
@@ -354,34 +373,37 @@ def scrap_chapter(title, novel_base_dir, chapter_url, chapter_num, update_html):
354
373
  click.echo(chapter.chapter_content)
355
374
 
356
375
  @cli.command()
376
+ @click.pass_context
357
377
  @title_option
358
- @novel_base_dir_option
359
378
  @sync_toc_option
360
379
  @click.option('--update-html', is_flag=True, default=False, show_default=True, help='If the chapter HTML is saved, it will be updated.')
361
380
  @click.option('--clean-chapters', is_flag=True, default=False, show_default=True, help='If the chapter HTML should be cleaned upon saving.')
362
- def request_all_chapters(title, novel_base_dir, sync_toc, update_html, clean_chapters):
381
+ def request_all_chapters(ctx, title, sync_toc, update_html, clean_chapters):
363
382
  """Request all chapters of a novel."""
364
- novel = obtain_novel(title, novel_base_dir)
383
+ novel = obtain_novel(title, ctx.obj)
365
384
  novel.request_all_chapters(
366
385
  sync_toc=sync_toc, update_html=update_html, clean_chapters=clean_chapters)
386
+ novel.save_novel()
367
387
  click.echo('All chapters requested and saved.')
368
388
 
369
389
  @cli.command()
390
+ @click.pass_context
370
391
  @title_option
371
- @novel_base_dir_option
372
- def show_chapters(title, novel_base_dir):
392
+ def show_chapters(ctx, title):
373
393
  """Show chapters of a novel."""
374
- novel = obtain_novel(title, novel_base_dir)
394
+ novel = obtain_novel(title, ctx.obj)
375
395
  click.echo(novel.show_chapters())
396
+ click.echo(f'Config file: {ctx.obj["CONFIG_FILE"]}')
397
+
376
398
 
377
399
  @cli.command()
400
+ @click.pass_context
378
401
  @title_option
379
- @novel_base_dir_option
380
402
  @sync_toc_option
381
403
  @click.option('--start-chapter', type=int, default=1, show_default=True, help='The start chapter for the books (position in the TOC, may differ from the actual number).')
382
404
  @click.option('--end-chapter', type=int, default=None, show_default=True, help='The end chapter for the books (if not defined, every chapter will be saved).')
383
405
  @click.option('--chapters-by-book', type=int, default=100, show_default=True, help='The number of chapters each book will have.')
384
- def save_novel_to_epub(title, novel_base_dir, sync_toc, start_chapter, end_chapter, chapters_by_book):
406
+ def save_novel_to_epub(ctx, title, sync_toc, start_chapter, end_chapter, chapters_by_book):
385
407
  """Save the novel to EPUB format."""
386
408
  if start_chapter <= 0:
387
409
  raise click.BadParameter(
@@ -395,7 +417,7 @@ def save_novel_to_epub(title, novel_base_dir, sync_toc, start_chapter, end_chapt
395
417
  raise click.BadParameter(
396
418
  'Should be a positive number.', param_hint='--chapters-by-book')
397
419
 
398
- novel = obtain_novel(title, novel_base_dir)
420
+ novel = obtain_novel(title, ctx.obj)
399
421
  if novel.save_novel_to_epub(sync_toc=sync_toc, start_chapter=start_chapter, end_chapter=end_chapter, chapters_by_book=chapters_by_book):
400
422
  click.echo('All books saved.')
401
423
  else:
@@ -404,27 +426,27 @@ def save_novel_to_epub(title, novel_base_dir, sync_toc, start_chapter, end_chapt
404
426
  # UTILS
405
427
 
406
428
  @cli.command()
429
+ @click.pass_context
407
430
  @title_option
408
- @novel_base_dir_option
409
431
  @click.option('--clean-chapters', is_flag=True, default=False, show_default=True, help='If the chapters HTML files are cleaned.')
410
432
  @click.option('--clean-toc', is_flag=True, default=False, show_default=True, help='If the TOC files are cleaned.')
411
433
  @click.option('--hard-clean', is_flag=True, default=False, show_default=True, help='If the files are more deeply cleaned.')
412
- def clean_files(title, novel_base_dir, clean_chapters, clean_toc, hard_clean):
434
+ def clean_files(ctx, title, clean_chapters, clean_toc, hard_clean):
413
435
  """Clean files of a novel."""
414
436
  if not clean_chapters and not clean_toc:
415
437
  click.echo(
416
438
  'You must choose at least one of the options: --clean-chapters, --clean-toc.', err=True)
417
439
  return
418
- novel = obtain_novel(title, novel_base_dir)
440
+ novel = obtain_novel(title, ctx.obj)
419
441
  novel.clean_files(clean_chapters=clean_chapters,
420
442
  clean_toc=clean_toc, hard_clean=hard_clean)
421
443
 
422
444
  @cli.command()
445
+ @click.pass_context
423
446
  @title_option
424
- @novel_base_dir_option
425
- def show_novel_dir(title, novel_base_dir):
447
+ def show_novel_dir(ctx, title):
426
448
  """Show the directory where the novel is saved."""
427
- novel = obtain_novel(title, novel_base_dir)
449
+ novel = obtain_novel(title, ctx.obj)
428
450
  click.echo(novel.show_novel_dir())
429
451
 
430
452
  @cli.command()
@@ -0,0 +1,84 @@
1
+ import os
2
+ import json
3
+
4
+ import platformdirs
5
+ from dotenv import load_dotenv
6
+ from pathlib import Path
7
+ from typing import Optional
8
+
9
+ from .logger_manager import create_logger
10
+ from .utils import FileOps
11
+
12
+ load_dotenv()
13
+
14
+ CURRENT_DIR = Path(__file__).resolve().parent
15
+
16
+ app_author = "web-novel-scraper"
17
+ app_name = "web-novel-scraper"
18
+
19
+ # DEFAULT VALUES
20
+ SCRAPER_CONFIG_FILE = str(Path(platformdirs.user_config_dir(app_name, app_author)) / "config.json")
21
+ SCRAPER_BASE_NOVELS_DIR = platformdirs.user_data_dir(app_name, app_author)
22
+ SCRAPER_DECODE_GUIDE_FILE = str(CURRENT_DIR / 'decode_guide/decode_guide.json')
23
+
24
+ logger = create_logger("CONFIG MANAGER")
25
+
26
+
27
+ ## ORDER PRIORITY
28
+ ## 1. PARAMETER TO THE INIT FUNCTION
29
+ ## 2. ENVIRONMENT VARIABLE
30
+ ## 3. CONFIG FILE VALUE
31
+ ## 4. DEFAULT VALUE
32
+ class ScraperConfig:
33
+ base_novels_dir: str
34
+ decode_guide_file: str
35
+
36
+ def __init__(self,
37
+ config_file: str = None,
38
+ base_novels_dir: str = None,
39
+ decode_guide_file: str = None):
40
+ ## LOADING CONFIGURATION
41
+ config_file = self._get_config(default_value=SCRAPER_CONFIG_FILE,
42
+ config_file_value=None,
43
+ env_variable="SCRAPER_CONFIG_FILE",
44
+ parameter_value=config_file)
45
+
46
+ config_file = Path(config_file)
47
+ logger.debug(f'Obtaining configuration from file "{config_file}"')
48
+ config = self._load_config(config_file)
49
+
50
+ if config is None:
51
+ logger.debug('No configuration found on config file.')
52
+ logger.debug('If no other config option was set, the default configuration will be used.')
53
+ config = {}
54
+
55
+ ## SETTING CONFIGURATION VALUES
56
+
57
+ self.base_novels_dir = self._get_config(default_value=SCRAPER_BASE_NOVELS_DIR,
58
+ config_file_value=config.get("base_novels_dir"),
59
+ env_variable="SCRAPER_BASE_NOVELS_DIR",
60
+ parameter_value=base_novels_dir)
61
+
62
+ self.decode_guide_file = self._get_config(default_value=SCRAPER_DECODE_GUIDE_FILE,
63
+ config_file_value=config.get("decode_guide_file"),
64
+ env_variable="SCRAPER_DECODE_GUIDE_FILE",
65
+ parameter_value=decode_guide_file)
66
+
67
+ @staticmethod
68
+ def _get_config(default_value: str,
69
+ config_file_value: Optional[str],
70
+ env_variable: str,
71
+ parameter_value: Optional[str]) -> str:
72
+ return (
73
+ parameter_value
74
+ or os.getenv(env_variable)
75
+ or config_file_value
76
+ or default_value
77
+ )
78
+
79
+ @staticmethod
80
+ def _load_config(config_file: Path) -> Optional[dict]:
81
+ config = FileOps.read_json(config_file)
82
+ if config is None:
83
+ logger.debug(f'Could not load configuration from file "{config_file}". Skipping...')
84
+ return config