Repository: dipu-bd/lightnovel-crawler (language: python)

Issue:
Fixes and Sources
**Added:**
https://wujizun.com
https://novelmic.com
https://www.1ksy.com [#594]
http://readonlinenovels.com [#457]
https://arnovel.me [#895]
https://lightnovelshub.com [#900]
https://smnovels.com [#904]
https://www.wnmtl.org [#874]

**Fixed:**
foxaholic.py - fixed cover as it was not showing on some novels.
[#909] novelringan.py - fixed issues reported.
[#887] fu_kemao.py - fixed issues reported.
Error: 'charmap' codec can't decode byte 0x90
Whenever querying for a novel with the query function the message 

! Error: 'charmap' codec can't decode byte 0x90 in position 10915: character maps to <undefined>

appears. 
This occurs every time,  doesn't matter the text input whether it is a single word or a collection of words

**Let us know**
- Novel URL: unavailabe ( used in search function )
- App Version: 2.26.3
- Package: exe
- OS: Windows 10
![Capture](https://user-images.githubusercontent.com/8968740/119092531-35873680-ba17-11eb-9427-ab915647eef3.PNG)

after using -ll to check which of the sites were producing the error removing 1stkissnovel.love and noveltrench.com seemed to have fixed the issue
scribblehub: images and moonreader
with the latest versions that include images some chapters will not been shown in moonreader epub reader. for example

https://www.scribblehub.com/series/299930/the-caretaker-of-otherworldly-tenants/

with latest chapters 18 to 20 do not show and just have 1 blank page. load in to calibre and remove the image section from the bottom of the page(under authors notes) and save fixes it.
however use an older version of lncrawler such as 2.24.1 and then have calibre download and add the external images works fine.

also these chapters are reported as having a bad id but having calibre fix that does not fix the chapters.

the offending chapters do show in other readers so may not seem much of an issue however it does point to the image adding not being perfect.

large thanks for this app.  :)
(Light novel Translations)Error: List Index Out Of Range
**Describe the bug**
Error:List Index Out of Range

**Let us know**
- Novel URL: [e.g. https://lightnovelstranslations.com/charging-magic-with-a-smile/]
- App Version: 2.26.3]
- Package: pip
- OS: android

Fix this bug: https://meionovel.id/ can't opened
bot and exe can't save epub from webpage: https://meionovel.id/

novel link: https://meionovel.id/novel/talisman-emperor/

OS: android 9 & Windows 10
App: Bot & Exe
Pls Fix this bug: wrong parameter for worldnovel.online
**Describe the bug**
Hello devs, @yudilee , @dipu-bd ,
You seem to have taken the parameters of the web page incorrectly: https://www.worldnovel.online/. 
Novel link for checking:
https://www.worldnovel.online/novel/my-disciples-are-all-villains/

`<div class="cha-content " data-report-l1="3">`
`<div class="cha-words">`

The parameters above incorrectly captures the hidden chapter contents (in English).  

What should be Crawled is the contents in Indonesian (below).

`<div id='soop' class="post-content text-cloud font-sizer-sync">`

*this should be easy to fix quickly, hope it helps*...

**Just remove codes: `.cha-content`  and  `.cha-words`  from  `download_chapter_body` parameter.**

__Suggestion:__  you should also be able to remove the footnote  `www.worldnovel.online`


OS: bot and termux.
Error: need a bytes-like object
When I search for this book **"Archean Eon Art"** I get this error for some reason.

` ! Error: decoding to str: need a bytes-like object, NoneType found `

- App Version: 2.26.3
- Package: pip & github
- OS: Windows 10
Cleanup mangatoon chapter content
Remove all left slashes  `\`  not just  ` \-`  to  `-`  or  `\"`  to  `"` . 

Because there's many double slash there.  Such as  `\\*`  will become  `\*`  or `\\(`  becomes  `\(`. 

It would be safe to remove all single left slashes. 

It won't affect the Chapter contents.

![image](https://user-images.githubusercontent.com/5158124/118803977-e54c8f00-b8c5-11eb-8aa7-d3ebcd0290ce.png)



Discussion / hints:







@dipu-bd 
i don't know if python supports removing single left slash or not.  otherwise you can try to removing the double left slash.  it's also probably going to remove a single left slash.
This method works well in script, maybe even in python.

`chapter_content = [p.replace('\\', '') for p in chapter_content]`

Fix patch:
diff --git a/README.md b/README.md
index 14b5290e8..09d3d211a 100644
--- a/README.md
+++ b/README.md
@@ -328,6 +328,7 @@ You are very welcome to contribute in this project. You can:
 | https://amnesiactl.com                       |            |           |      @SirGryphin      |
 | https://anonanemone.wordpress.com            |            |           |      @SirGryphin      |
 | https://arangscans.com                       |            |           |      @SirGryphin      |
+| https://arnovel.me                           |     ✔      |           |      @SirGryphin      |
 | https://asadatranslations.com                |     ✔      |           |      @SirGryphin      |
 | https://automtl.wordpress.com                |            |           |      @SirGryphin      |
 | https://babelnovel.com                       |     ✔      |     ✔     |       @dipu-bd        |
@@ -336,6 +337,7 @@ You are very welcome to contribute in this project. You can:
 | https://booknet.com                          |            |           | @dipu-bd, @SirGryphin |
 | https://boxnovel.com                         |     ✔      |           |       @dipu-bd        |
 | https://boxnovel.online                      |     ✔      |           |      @SirGryphin      |
+| https://cclawtranslations.home.blog          |            |           |      @SirGryphin      |
 | https://clicknovel.net                       |            |           |      @SirGryphin      |
 | https://creativenovels.com                   |            |           |                       |
 | https://crescentmoon.blog                    |            |           |                       |
@@ -343,13 +345,13 @@ You are very welcome to contribute in this project. You can:
 | https://darktranslation.com                  |            |           |      @SirGryphin      |
 | https://demontranslations.com                |            |           |      @SirGryphin      |
 | https://dmtranslationscn.com                 |            |           |      @SirGryphin      |
+| https://dobelyuwai.wordpress.com             |            |           |      @SirGryphin      |
 | https://docln.net                            |     ✔      |           |       @dipu-bd        |
 | https://dsrealmtranslations.com              |            |           |      @SirGryphin      |
 | https://es.mtlnovel.com                      |     ✔      |           |                       |
 | https://exiledrebelsscanlations.com          |            |           |      @SirGryphin      |
 | https://fanstranslations.com                 |            |           |      @SirGryphin      |
 | https://fastnovel.net                        |     ✔      |           |      @SirGryphin      |
-| https://foxaholic.com                        |     ✔      |           |                       |
 | https://fr.mtlnovel.com                      |     ✔      |           |                       |
 | https://fujitranslation.com                  |            |           |      @SirGryphin      |
 | https://hui3r.wordpress.com                  |            |           |      @SirGryphin      |
@@ -358,11 +360,13 @@ You are very welcome to contribute in this project. You can:
 | https://indowebnovel.id                      |            |           |                       |
 | https://infinitenoveltranslations.net        |            |           |      @SirGryphin      |
 | https://instadoses.com                       |            |           |                       |
+| https://isotls.com                           |            |           |      @SirGryphin      |
 | https://jpmtl.com                            |            |           |       @dipu-bd        |
 | https://jstranslations1.com                  |            |           |      @SirGryphin      |
 | https://justatranslatortranslations.com      |            |           |      @SirGryphin      |
 | https://kiss-novel.com                       |            |           |                       |
 | https://kisslightnovels.info                 |     ✔      |           |                       |
+| https://lazybirdtranslations.wordpress.com   |            |           |      @SirGryphin      |
 | https://lemontreetranslations.wordpress.com  |            |           |      @SirGryphin      |
 | https://light-novel.online                   |     ✔      |           |                       |
 | https://lightnovel.tv                        |     ✔      |           |      @SirGryphin      |
@@ -370,6 +374,7 @@ You are very welcome to contribute in this project. You can:
 | https://lightnovelbastion.com                |            |           |       @dipu-bd        |
 | https://lightnovelheaven.com                 |            |           |      @SirGryphin      |
 | https://lightnovelkiss.com                   |     ✔      |           |      @SirGryphin      |
+| https://lightnovelshub.com                   |     ✔      |           |      @SirGryphin      |
 | https://lightnovelsonl.com                   |     ✔      |           |      @SirGryphin      |
 | https://lightnovelstranslations.com          |            |           |      @SirGryphin      |
 | https://listnovel.com                        |     ✔      |           |                       |
@@ -397,6 +402,7 @@ You are very welcome to contribute in this project. You can:
 | https://novelfullplus.com                    |     ✔      |           | @dipu-bd, @SirGryphin |
 | https://novelgate.net                        |     ✔      |           |      @SirGryphin      |
 | https://novelgo.id/                          |            |           |                       |
+| https://novelmic.com                         |     ✔      |           |      @SirGryphin      |
 | https://novelonlinefree.com                  |     ✔      |           |      @SirGryphin      |
 | https://novelonlinefull.com                  |     ✔      |           |                       |
 | https://novelraw.blogspot.com                |            |           |                       |
@@ -410,12 +416,17 @@ You are very welcome to contribute in this project. You can:
 | https://overabook.com                        |     ✔      |           |      @SirGryphin      |
 | https://ranobelib.me                         |            |           |                       |
 | https://readlightnovels.net                  |     ✔      |           |     @PreownedFIN      |
+| https://readnovelz.net                       |     ✔      |           |      @SirGryphin      |
+| http://readonlinenovels.com                  |            |           |                       |
 | https://readwebnovels.net                    |     ✔      |           |      @SirGryphin      |
 | https://reincarnationpalace.com              |            |           |      @SirGryphin      |
 | https://rewayat.club                         |            |           |                       |
+| https://rpgnovels.com                        |            |           |      @SirGryphin      |
+| https://rpgnoob.wordpress.com                |            |           |      @SirGryphin      |
 | https://shalvationtranslations.wordpress.com |            |           |      @SirGryphin      |
 | https://skynovel.org/                        |            |           |      @SirGryphin      |
 | https://sleepytranslations.com               |            |           |      @SirGryphin      |
+| https://smnovels.com                         |            |           |      @SirGryphin      |
 | https://steambunlightnovel.com               |            |           |      @SirGryphin      |
 | https://supernovel.net                       |     ✔      |           |      @SirGryphin      |
 | https://toc.qidianunderground.org            |     ✔      |           |       @dipu-bd        |
@@ -434,18 +445,21 @@ You are very welcome to contribute in this project. You can:
 | https://woopread.com                         |     ✔      |           |                       |
 | https://wordexcerpt.com                      |     ✔      |           | @dipu-bd, @SirGryphin |
 | https://wordexcerpt.org                      |            |           | @dipu-bd, @SirGryphin |
+| https://wujizun.com                          |            |           |      @SirGryphin      |
 | https://wuxiaworld.io                        |     ✔      |           |      @SirGryphin      |
 | https://wuxiaworld.live                      |     ✔      |           |      @SirGryphin      |
 | https://wuxiaworld.name                      |     ✔      |           |      @SirGryphin      |
 | https://wuxiaworld.online                    |     ✔      |           |       @dipu-bd        |
 | https://wuxiaworld.site                      |            |           |       @dipu-bd        |
 | https://wuxiaworldsite.co                    |            |           |       @dipu-bd        |
+| https://www.1ksy.com                         |            |           |      @SirGryphin      |
 | https://www.aixdzs.com                       |            |           |                       |
 | https://www.asianhobbyist.com                |            |           |                       |
 | https://www.box-novel.com                    |     ✔      |           |      @SirGryphin      |
 | https://www.daocaorenshuwu.com               |            |           |      @SirGryphin      |
 | https://www.f-w-o.com                        |     ✔      |           |      @SirGryphin      |
 | https://www.flying-lines.com                 |            |           |       @dipu-bd        |
+| https://www.foxaholic.com                    |     ✔      |           |      @SirGryphin      |
 | https://www.foxteller.com                    |     ✔      |           |       @dipu-bd        |
 | https://www.freelightnovel.com               |            |           |      @SirGryphin      |
 | https://www.fuyuneko.org                     |            |           |      @SirGryphin      |
@@ -461,12 +475,14 @@ You are very welcome to contribute in this project. You can:
 | https://www.novelcool.com                    |            |           |      @SirGryphin      |
 | https://www.novelhall.com                    |            |           |                       |
 | https://www.novelhunters.com                 |     ✔      |           |      @SirGryphin      |
+| https://www.novelmultiverse.com              |     ✔      |           |      @SirGryphin      |
 | https://www.novelpassion.com                 |     ✔      |           |      @SirGryphin      |
 | https://www.novelringan.com                  |            |           |                       |
 | https://www.novels.pl                        |            |           |       @dipu-bd        |
 | https://www.novelspread.com                  |            |           |                       |
 | https://www.novelupdates.cc                  |            |           |      @SirGryphin      |
 | https://www.oppatranslations.com             |            |           |      @SirGryphin      |
+| https://www.ornovel.com                      |            |           |      @SirGryphin      |
 | https://www.qidian.com                       |            |           |                       |
 | https://www.readlightnovel.cc                |            |           |                       |
 | https://www.readlightnovel.org               |            |           |                       |
@@ -482,6 +498,7 @@ You are very welcome to contribute in this project. You can:
 | https://www.webnovel.com                     |     ✔      |           |       @dipu-bd        |
 | https://www.webnovelover.com                 |     ✔      |           |      @SirGryphin      |
 | https://www.worldnovel.online                |     ✔      |           |                       |
+| https://www.wnmtl.org                        |            |           |      @SirGryphin      |
 | https://www.wuxialeague.com                  |            |           |                       |
 | https://www.wuxiaworld.co                    |     ✔      |           |       @dipu-bd        |
 | https://www.wuxiaworld.com                   |     ✔      |           |       @dipu-bd        |
diff --git a/lncrawl/VERSION b/lncrawl/VERSION
index 3953e8ad5..cc6ac9cc5 100644
--- a/lncrawl/VERSION
+++ b/lncrawl/VERSION
@@ -1,1 +1,1 @@
-2.26.3
+2.26.4
diff --git a/lncrawl/assets/html_style.py b/lncrawl/assets/html_style.py
index 6c6dd92c6..d752d6b63 100644
--- a/lncrawl/assets/html_style.py
+++ b/lncrawl/assets/html_style.py
@@ -4,7 +4,7 @@
 
 ROOT = Path(__file__).parent
 
-with open(str(ROOT / 'html_style.css'), 'r') as f:
+with open(str(ROOT / 'html_style.css'), 'r', encoding='utf8') as f:
     style = f.read()
 # end with
 
diff --git a/lncrawl/assets/version.py b/lncrawl/assets/version.py
index 78a885b62..6ededd60a 100644
--- a/lncrawl/assets/version.py
+++ b/lncrawl/assets/version.py
@@ -3,7 +3,7 @@
 
 ROOT = Path(__file__).parent.parent
 
-with open(str(ROOT / 'VERSION'), 'r') as f:
+with open(str(ROOT / 'VERSION'), 'r', encoding='utf8') as f:
     version = f.read().strip()
 # end with
 
diff --git a/lncrawl/binders/calibre.py b/lncrawl/binders/calibre.py
index 36ab0918b..dac94254d 100644
--- a/lncrawl/binders/calibre.py
+++ b/lncrawl/binders/calibre.py
@@ -16,7 +16,7 @@ def run_ebook_convert(*args):
     '''
     try:
         isdebug = os.getenv('debug_mode') == 'yes'
-        with open(os.devnull, 'w') as dumper:
+        with open(os.devnull, 'w', encoding='utf8') as dumper:
             subprocess.call(
                 [EBOOK_CONVERT] + list(args),
                 stdout=None if isdebug else dumper,
diff --git a/lncrawl/binders/text.py b/lncrawl/binders/text.py
index 34f0557d3..a5a47a633 100644
--- a/lncrawl/binders/text.py
+++ b/lncrawl/binders/text.py
@@ -15,7 +15,7 @@ def make_texts(app, data):
         for chap in data[vol]:
             file_name = '%s.txt' % str(chap['id']).rjust(5, '0')
             file_name = os.path.join(dir_name, file_name)
-            with open(file_name, 'w', encoding='utf-8') as file:
+            with open(file_name, 'w', encoding='utf8') as file:
                 body = chap['body'].replace('</p><p', '</p>\n<p')
                 soup = BeautifulSoup(body, 'lxml')
                 text = '\n\n'.join(soup.stripped_strings)
diff --git a/lncrawl/binders/web.py b/lncrawl/binders/web.py
index 93d20595b..9a1e8c838 100644
--- a/lncrawl/binders/web.py
+++ b/lncrawl/binders/web.py
@@ -70,7 +70,7 @@ def make_webs(app, data):
                 chapter, prev_chapter, next_chapter, direction)
 
             file_name = os.path.join(dir_name, file_name)
-            with open(file_name, 'w', encoding='utf-8') as file:
+            with open(file_name, 'w', encoding='utf8') as file:
                 file.write(html)
             # end with
             web_files.append(file_name)
diff --git a/lncrawl/bots/discord/config.py b/lncrawl/bots/discord/config.py
index c54ef59c8..e32b0a36e 100644
--- a/lncrawl/bots/discord/config.py
+++ b/lncrawl/bots/discord/config.py
@@ -44,7 +44,7 @@
             'filename': 'logs/discord-bot_%s.log' % (get_args().shard_id),
             'maxBytes': 10 * 1024 * 1024,  # 10 MB
             'backupCount': 5,
-            'encoding': 'utf-8',
+            'encoding': 'utf8',
         },
     },
     'loggers': {
diff --git a/lncrawl/bots/discord/discord_bot.py b/lncrawl/bots/discord/discord_bot.py
index 455adf1b9..30a0b1b92 100644
--- a/lncrawl/bots/discord/discord_bot.py
+++ b/lncrawl/bots/discord/discord_bot.py
@@ -7,7 +7,6 @@
 
 import discord
 
-from ...assets.version import get_value as get_version
 from ...core.arguments import get_args
 from .config import signal
 from .message_handler import MessageHandler
@@ -15,8 +14,17 @@
 logger = logging.getLogger(__name__)
 
 
+def get_bot_version():
+    head_path = os.path.join('.git', 'ORIG_HEAD')
+    if not os.path.isfile(head_path):
+        from ...assets.version import get_value
+        return get_value
+    with open(head_path, 'r', encoding='utf8') as f:
+        return f.readline()[:7]
+
+
 class DiscordBot(discord.Client):
-    bot_version = get_version() + '_8'
+    bot_version = get_bot_version()
 
     def __init__(self, *args, loop=None, **options):
         options['shard_id'] = get_args().shard_id
@@ -38,7 +46,7 @@ async def on_ready(self):
         self.handlers = {}
 
         print('Discord bot in online!')
-        activity = discord.Activity(name='for 🔥%s🔥 (v%s)' % (signal, self.bot_version),
+        activity = discord.Activity(name='for 🔥%s🔥 (%s)' % (signal, self.bot_version),
                                     type=discord.ActivityType.watching)
         await self.change_presence(activity=activity,
                                    status=discord.Status.online)
diff --git a/lncrawl/core/downloader.py b/lncrawl/core/downloader.py
index 4af7134b3..edb0c7cf1 100644
--- a/lncrawl/core/downloader.py
+++ b/lncrawl/core/downloader.py
@@ -75,13 +75,13 @@ def generate_cover(app):
             author=app.crawler.novel_author,
         )
 
-        with open(svg_file, 'w', encoding='utf-8') as f:
+        with open(svg_file, 'w', encoding='utf8') as f:
             f.write(svg)
             logger.debug('Saved a random cover.svg')
         # end with
 
         png_file = os.path.join(app.output_path, 'cover.png')
-        svg2png(bytestring=svg.encode('utf-8'), write_to=png_file)
+        svg2png(bytestring=svg.encode('utf8'), write_to=png_file)
         logger.debug('Converted cover.svg to cover.png')
 
         return png_file
@@ -145,7 +145,7 @@ def download_chapter_body(app, chapter):
 
         os.makedirs(dir_name, exist_ok=True)
         with open(file_name, 'w', encoding="utf-8") as file:
-            file.write(json.dumps(chapter))
+            file.write(json.dumps(chapter, ensure_ascii=False))
         # end with
     # end if
 
@@ -286,7 +286,11 @@ def download_chapter_images(app):
             if img['src'] in images:
                 filename = images[img['src']]
                 img['src'] = 'images/%s' % filename
-                img['style'] = 'float: left; margin: 15px; width: 100%;'
+                #img['style'] = 'float: left; margin: 15px; width: 100%;'
+
+            # Issue 920: styles are causing issues in some readers
+            if hasattr(img, 'attr'):
+                img.attr = {k: v for k, v in img.attrs.items() if k == 'src'}
         # end for
         chapter['body'] = str(soup.select_one('main'))
     # end for
diff --git a/lncrawl/core/novel_info.py b/lncrawl/core/novel_info.py
index 60829174d..fabf3c796 100644
--- a/lncrawl/core/novel_info.py
+++ b/lncrawl/core/novel_info.py
@@ -93,6 +93,6 @@ def save_metadata(app, completed=False):
     os.makedirs(app.output_path, exist_ok=True)
     file_name = os.path.join(app.output_path, C.META_FILE_NAME)
     with open(file_name, 'w', encoding="utf-8") as file:
-        json.dump(data, file, indent=2)
+        json.dump(data, file, indent=2, ensure_ascii=False)
     # end with
 # end def
diff --git a/lncrawl/core/novel_search.py b/lncrawl/core/novel_search.py
index d9b7a8b45..295b2baf5 100644
--- a/lncrawl/core/novel_search.py
+++ b/lncrawl/core/novel_search.py
@@ -39,7 +39,7 @@ def get_search_result(app, link, bar):
 def process_results(combined_results):
     combined = dict()
     for result in combined_results:
-        key = slugify(result['title'])
+        key = slugify(result.get('title') or '')
         if len(key) <= 2:
             continue
         combined.setdefault(key, [])
diff --git a/lncrawl/sources/1ksy.py b/lncrawl/sources/1ksy.py
new file mode 100644
index 000000000..1492f21f8
--- /dev/null
+++ b/lncrawl/sources/1ksy.py
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+import logging
+import re
+from bs4 import BeautifulSoup
+from ..utils.crawler import Crawler
+
+logger = logging.getLogger(__name__)
+
+ajaxchapter_url = 'https://www.1ksy.com/home/index/ajaxchapter'
+
+
+class OneKsyCrawler(Crawler):
+    base_url = [
+        'https://m.1ksy.com/',
+        'https://www.1ksy.com/',
+    ]
+
+    def initialize(self):
+        self.home_url = 'https://www.1ksy.com/'
+    # end def
+
+    def read_novel_info(self):
+        '''Get novel title, autor, cover etc'''
+        url = self.novel_url.replace(
+            'https://m.1ksy', 'https://www.1ksy')
+        logger.debug('Visiting %s', url)
+        soup = self.get_soup(url)
+
+        self.novel_title = soup.select_one(
+            'body > div.jieshao > div.rt > h1').text.strip()
+        logger.info('Novel title: %s', self.novel_title)
+
+        self.novel_cover = self.absolute_url(
+            soup.select_one('body > div > div.lf > img')['src'])
+        logger.info('Novel cover: %s', self.novel_cover)
+
+        self.novel_author = soup.select_one(
+            'meta[property="og:novel:author"]')['content']
+        logger.info('Novel author: %s', self.novel_author)
+
+        chap_id = 0
+        for a in soup.select('body > div.mulu ul')[-1].select('li a'):
+            vol_id = chap_id // 100 + 1
+            if vol_id > len(self.volumes):
+                self.volumes.append({
+                    'id': vol_id,
+                    'title': 'Volume %d' % vol_id
+                })
+            # end if
+
+            chap_id += 1
+            self.chapters.append({
+                'id': chap_id,
+                'volume': vol_id,
+                'title': a['title'],
+                'url': self.absolute_url(a['href']),
+            })
+        # end for
+    # end def
+
+    def download_chapter_body(self, chapter):
+        '''Download body of a single chapter and return as clean html format.'''
+        logger.info('Visiting %s', chapter['url'])
+        soup = self.get_soup(chapter['url'])
+        chapter['body_lock'] = True
+        contents = soup.select_one('#content')
+        body = self.extract_contents(contents)
+        return '<p>' + '</p><p>'.join(body) + '</p>'
+    # end def
+# end class
\ No newline at end of file
diff --git a/lncrawl/sources/88tang.py b/lncrawl/sources/88tang.py
index af08340c6..a9fb32227 100644
--- a/lncrawl/sources/88tang.py
+++ b/lncrawl/sources/88tang.py
@@ -15,20 +15,20 @@ def read_novel_info(self):
         logger.debug('Visiting %s', self.novel_url)
         soup = self.get_soup(self.novel_url)
 
-        self.novel_title = soup.find("h1", {"class": "entry-title"}).text.strip()
-        logger.info('Novel title: %s', self.novel_title)
-
-        # NOTE: Site list no cover images.
-        # self.novel_cover = self.absolute_url(
-        #     soup.select_one('div.entry-content p img')['src'])
-        # logger.info('Novel cover: %s', self.novel_cover)
+        title = soup.select_one('h1.entry-title').text
+        self.novel_title = title.rsplit('~', 1)[0].strip()
+        logger.debug('Novel title = %s', self.novel_title)
 
         self.novel_author = "by 88 Tang"
         logger.info('Novel author: %s', self.novel_author)
 
+        # Removes none TOC links.
+        toc_parts = soup.select_one('.entry-content')
+        for notoc in toc_parts.select('.sharedaddy, .code-block, script, .adsbygoogle'):
+            notoc.decompose()
+
         # Extract volume-wise chapter entries
-        # FIXME: Sometimes grabs social media link at bottom of page, No idea how to exclude links.
-        # FIXME: Chapter title are url links, it's the way translator formatted website.
+        # TODO: Chapter title are url links, it's the way translator formatted website.
         chapters = soup.select('.entry-content a[href*="88tangeatdrinkread.wordpress.com"]')
 
         for a in chapters:
@@ -55,8 +55,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         body = []
         contents = soup.select('div.entry-content p')
         for p in contents:
diff --git a/lncrawl/sources/__init__.py b/lncrawl/sources/__init__.py
index 708f269ed..8f0e2c8b2 100644
--- a/lncrawl/sources/__init__.py
+++ b/lncrawl/sources/__init__.py
@@ -22,7 +22,7 @@
 rejected_sources = {
     'https://chrysanthemumgarden.com/': 'Removed on request of the owner (Issue #649)',
     'https://novelplanet.com/': 'Site is closed',
-    'http://gravitytales.com/': 'Redirects to webnovel.com',
+    'http://gravitytales.com/': 'Domain is expired',
     'http://fullnovel.live/': "403 - Forbidden: Access is denied",
     'http://moonbunnycafe.com/': "Does not follow uniform format",
     'https://anythingnovel.com/': 'Site broken',
@@ -42,6 +42,11 @@
     'https://fsapk.com/': 'Site is not working',
     'https://bestoflightnovels.com/': 'Site moved',
     'https://novelcrush.com/': 'Site is down',
+    'https://888novel.com/': 'Gets IP banned for using crawler',
+    'https://novelraw.blogspot.com/': 'Site closed down',
+    'https://www.novelspread.com/': 'Site is down',
+    'https://www.translateindo.com/': 'Site is down',
+    'https://dsrealmtranslations.com/': 'Site is down'
 }
 
 # this list will be auto-generated
diff --git a/lncrawl/sources/arnovel.py b/lncrawl/sources/arnovel.py
new file mode 100644
index 000000000..7615472bb
--- /dev/null
+++ b/lncrawl/sources/arnovel.py
@@ -0,0 +1,86 @@
+# -*- coding: utf-8 -*-
+import logging
+from urllib.parse import urlparse
+from ..utils.crawler import Crawler
+
+logger = logging.getLogger(__name__)
+search_url = 'https://arnovel.me/?s=%s&post_type=wp-manga&author=&artist=&release='
+chapter_list_url = 'https://arnovel.me/wp-admin/admin-ajax.php'
+
+class ArNovelCrawler(Crawler):
+    base_url = 'https://arnovel.me/'
+
+    def search_novel(self, query):
+        query = query.lower().replace(' ', '+')
+        soup = self.get_soup(search_url % query)
+
+        # Add "title" to use alterntive name so you can search english name as search for arabic title doesn't work.
+        results = []
+        for tab in soup.select('.c-tabs-item__content'):
+            a = tab.select_one('.post-title h3 a')
+            title = tab.select_one('.mg_alternative .summary-content')
+            latest = tab.select_one('.latest-chap .chapter a').text
+            votes = tab.select_one('.rating .total_votes').text
+            results.append({
+                'title': title.text.strip(),
+                'url': self.absolute_url(a['href']),
+                'info': '%s | Rating: %s' % (latest, votes),
+            })
+        # end for
+
+        return results
+    # end def
+
+    def read_novel_info(self):
+        '''Get novel title, autor, cover etc'''
+        logger.debug('Visiting %s', self.novel_url)
+        soup = self.get_soup(self.novel_url)
+
+        possible_title = soup.select_one('.post-title h1')
+        for span in possible_title.select('span'):
+            span.extract()
+        # end for
+        self.novel_title = possible_title.text.strip()
+        logger.info('Novel title: %s', self.novel_title)
+
+        # No novel covers on site.
+        # self.novel_cover = self.absolute_url(
+        #     soup.select_one('.summary_image a img')['data-src'])
+        # logger.info('Novel cover: %s', self.novel_cover)
+
+        self.novel_author = ' '.join([
+            a.text.strip()
+            for a in soup.select('.author-content a[href*="novel-author"]')
+        ])
+        logger.info('%s', self.novel_author)
+
+        self.novel_id = soup.select_one('#manga-chapters-holder')['data-id']
+        logger.info('Novel id: %s', self.novel_id)
+
+        response = self.submit_form(
+            chapter_list_url, data='action=manga_get_chapters&manga=' + self.novel_id)
+        soup = self.make_soup(response)
+        for a in reversed(soup.select('.wp-manga-chapter a')):
+            chap_id = len(self.chapters) + 1
+            vol_id = 1 + len(self.chapters) // 100
+            if chap_id % 100 == 1:
+                self.volumes.append({'id': vol_id})
+            # end if
+            self.chapters.append({
+                'id': chap_id,
+                'volume': vol_id,
+                'title': a.text.strip(),
+                'url':  self.absolute_url(a['href']),
+            })
+        # end for
+    # end def
+
+    def download_chapter_body(self, chapter):
+        '''Download body of a single chapter and return as clean html format.'''
+        logger.info('Visiting %s', chapter['url'])
+        soup = self.get_soup(chapter['url'])
+        chapter['body_lock'] = True
+        contents = soup.select('.reading-content p')
+        return ''.join([str(p) for p in contents])
+    # end def
+# end class
\ No newline at end of file
diff --git a/lncrawl/sources/asadatrans.py b/lncrawl/sources/asadatrans.py
index 6331b4bee..9ccf0e526 100644
--- a/lncrawl/sources/asadatrans.py
+++ b/lncrawl/sources/asadatrans.py
@@ -42,10 +42,9 @@ def read_novel_info(self):
         self.novel_title = possible_title.text.strip()
         logger.info('Novel title: %s', self.novel_title)
 
-        # NOTE: Site doesn't have book covers.
-        # self.novel_cover = self.absolute_url(
-        #     soup.select_one('.summary_image a img')['src'])
-        # logger.info('Novel cover: %s', self.novel_cover)
+        self.novel_cover = soup.select_one(
+            'meta[property="og:image"]')['content']
+        logger.info('Novel cover: %s', self.novel_cover)
 
         self.novel_author = ' '.join([
             a.text.strip()
diff --git a/lncrawl/sources/automtl.py b/lncrawl/sources/automtl.py
index 54e8caad6..6181e1c3a 100644
--- a/lncrawl/sources/automtl.py
+++ b/lncrawl/sources/automtl.py
@@ -55,8 +55,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         body_parts = soup.select_one('div.entry-content')
 
         # Removes "Share this" text and buttons from bottom of chapters.
diff --git a/lncrawl/sources/bestlightnovel.py b/lncrawl/sources/bestlightnovel.py
index bbd761d55..5893fa91c 100644
--- a/lncrawl/sources/bestlightnovel.py
+++ b/lncrawl/sources/bestlightnovel.py
@@ -69,8 +69,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         if 'Chapter' in soup.select_one('h1').text:
             chapter['title'] = soup.select_one('h1').text
         else:
@@ -83,6 +81,8 @@ def download_chapter_body(self, chapter):
         ]
 
         contents = soup.select_one('#vung_doc')
+        for bad in contents.select('br'):
+            bad.decompose()
         body = self.extract_contents(contents)
         return '<p>' + '</p><p>'.join(body) + '</p>'
     # end def
diff --git a/lncrawl/sources/boxnovelorg.py b/lncrawl/sources/boxnovelorg.py
index 2e384a92c..dd6fe8680 100644
--- a/lncrawl/sources/boxnovelorg.py
+++ b/lncrawl/sources/boxnovelorg.py
@@ -13,7 +13,7 @@ class BoxNovelOrgCrawler(Crawler):
 
     def search_novel(self, query):
         query = query.lower().replace(' ', '+')
-        soup = self.get_soup(search_url % query)
+        soup = self.get_soup(search_url % query, verify=False)
 
         results = []
         for tab in soup.select('.col-novel-main .list-novel .row'):
diff --git a/lncrawl/sources/cclawtranslations.py b/lncrawl/sources/cclawtranslations.py
new file mode 100644
index 000000000..09116bc85
--- /dev/null
+++ b/lncrawl/sources/cclawtranslations.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+import json
+import logging
+import re
+from ..utils.cleaner import cleanup_text
+from ..utils.crawler import Crawler
+
+logger = logging.getLogger(__name__)
+
+class CclawTranslations(Crawler):
+    base_url = 'https://cclawtranslations.home.blog/'
+
+    def read_novel_info(self):
+        '''Get novel title, autor, cover etc'''
+        logger.debug('Visiting %s', self.novel_url)
+        soup = self.get_soup(self.novel_url)
+
+        title = soup.select_one(
+            'meta[property="og:title"]')['content']
+        self.novel_title = title.rsplit(' ', 1)[0].strip()
+        logger.debug('Novel title = %s', self.novel_title)
+
+        self.novel_cover = soup.select_one(
+            'meta[property="og:image"]')['content']
+        logger.info('Novel cover: %s', self.novel_cover)
+
+        self.novel_author = soup.select_one(
+            'meta[property="og:site_name"]')['content']
+        logger.info("%s", self.novel_author)
+
+        # Removes none TOC links from bottom of page.
+        toc_parts = soup.select_one('div.entry-content')
+        for notoc in toc_parts.select('.sharedaddy, .inline-ad-slot, .code-block, script, hr, .adsbygoogle'):
+            notoc.decompose()
+
+        # Extract volume-wise chapter entries
+        # Stops external links being selected as chapters
+        chapters = soup.select(
+            'div.entry-content a[href*="cclawtranslations.home.blog/2"]')
+
+        for a in chapters:
+            chap_id = len(self.chapters) + 1
+            if len(self.chapters) % 100 == 0:
+                vol_id = chap_id//100 + 1
+                vol_title = 'Volume ' + str(vol_id)
+                self.volumes.append({
+                    'id': vol_id,
+                    'title': vol_title,
+                })
+            # end if
+            self.chapters.append({
+                'id': chap_id,
+                'volume': vol_id,
+                'url':  self.absolute_url(a['href']),
+                'title': a.text.strip() or ('Chapter %d' % chap_id),
+            })
+        # end for
+    # end def
+
+    @cleanup_text
+    def download_chapter_body(self, chapter):
+        '''Download body of a single chapter and return as clean html format.'''
+        logger.info('Downloading %s', chapter['url'])
+        soup = self.get_soup(chapter['url'])
+
+        body_parts = soup.select_one('div.entry-content')
+
+        # Removes "Share this" text and buttons from bottom of chapters. Also other junk on page.
+        for share in body_parts.select('.sharedaddy, .inline-ad-slot, .code-block, script, hr, .adsbygoogle'):
+            share.decompose()
+
+        # Fixes images, so they can be downloaded.
+        all_imgs = soup.find_all('img')
+        for img in all_imgs:
+            if img.has_attr('data-orig-file'):
+                src_url = img['src']
+                parent = img.parent
+                img.decompose()
+                new_tag = soup.new_tag("img", src=src_url)
+                parent.append(new_tag)
+
+        return str(body_parts)
+    # end def
+# end class
\ No newline at end of file
diff --git a/lncrawl/sources/creativenovels.py b/lncrawl/sources/creativenovels.py
index c5b683489..055bb7fcf 100644
--- a/lncrawl/sources/creativenovels.py
+++ b/lncrawl/sources/creativenovels.py
@@ -72,7 +72,7 @@ def read_novel_info(self):
                 s=list_security_key
             )
         )
-        self.parse_chapter_list(response.content.decode('utf-8'))
+        self.parse_chapter_list(response.content.decode('utf8'))
     # end def
 
     def parse_chapter_list(self, content):
diff --git a/lncrawl/sources/crescentmoon.py b/lncrawl/sources/crescentmoon.py
index af1c38144..fb64683c6 100644
--- a/lncrawl/sources/crescentmoon.py
+++ b/lncrawl/sources/crescentmoon.py
@@ -58,8 +58,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         # if soup.find("h1", {"class": "entry-title"}).text.strip():
         #    chapter['title'] = soup.find("h1", {"class": "entry-title"}).text.strip()
         # else:
diff --git a/lncrawl/sources/darktrans.py b/lncrawl/sources/darktrans.py
index 7c3bdb378..75a77755f 100644
--- a/lncrawl/sources/darktrans.py
+++ b/lncrawl/sources/darktrans.py
@@ -54,8 +54,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         body = []
         contents = soup.select('div.entry-content p')
         for p in contents:
diff --git a/lncrawl/sources/demontrans.py b/lncrawl/sources/demontrans.py
index f34eebb79..0ef04f320 100644
--- a/lncrawl/sources/demontrans.py
+++ b/lncrawl/sources/demontrans.py
@@ -54,8 +54,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         body_parts = soup.select_one('div.entry-content')
 
         # Removes "Share this" text and buttons from bottom of chapters.
diff --git a/lncrawl/sources/dobelyuwai.py b/lncrawl/sources/dobelyuwai.py
new file mode 100644
index 000000000..bb00bd1bf
--- /dev/null
+++ b/lncrawl/sources/dobelyuwai.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+import json
+import logging
+import re
+from ..utils.cleaner import cleanup_text
+from ..utils.crawler import Crawler
+
+logger = logging.getLogger(__name__)
+
+class Dobelyuwai(Crawler):
+    base_url = 'https://dobelyuwai.wordpress.com/'
+
+    def read_novel_info(self):
+        '''Get novel title, autor, cover etc'''
+        logger.debug('Visiting %s', self.novel_url)
+        soup = self.get_soup(self.novel_url)
+
+        self.novel_title = soup.select_one(
+            'meta[property="og:title"]')['content']
+        logger.info('Novel title: %s', self.novel_title)
+
+        self.novel_cover = soup.select_one(
+            'meta[property="og:image"]')['content']
+        logger.info('Novel cover: %s', self.novel_cover)
+
+        self.novel_author = soup.select_one('div.entry-content > p:nth-child(2)').text
+        logger.info('%s', self.novel_author)
+
+        # Removes none TOC links from bottom of page.
+        toc_parts = soup.select_one('div.entry-content')
+
+        for notoc in toc_parts.select('.sharedaddy, .inline-ad-slot, .code-block, script, .adsbygoogle'):
+            notoc.decompose()
+
+        # Extract volume-wise chapter entries
+        # Stops external links being selected as chapters
+        chapters = soup.select(
+            'div.entry-content a[href*="https://dobelyuwai.wordpress.com/2"]')
+
+        for a in chapters:
+            chap_id = len(self.chapters) + 1
+            if len(self.chapters) % 100 == 0:
+                vol_id = chap_id//100 + 1
+                vol_title = 'Volume ' + str(vol_id)
+                self.volumes.append({
+                    'id': vol_id,
+                    'title': vol_title,
+                })
+            # end if
+            self.chapters.append({
+                'id': chap_id,
+                'volume': vol_id,
+                'url':  self.absolute_url(a['href']),
+                'title': a.text.strip() or ('Chapter %d' % chap_id),
+            })
+        # end for
+    # end def
+
+    @cleanup_text
+    def download_chapter_body(self, chapter):
+        '''Download body of a single chapter and return as clean html format.'''
+        logger.info('Downloading %s', chapter['url'])
+        soup = self.get_soup(chapter['url'])
+
+        body_parts = soup.select_one('div.entry-content')
+
+        # Removes "Share this" text and buttons from bottom of chapters. Also other junk on page.
+        for share in body_parts.select('.sharedaddy, .inline-ad-slot, .code-block, script, hr, .adsbygoogle'):
+            share.decompose()
+
+        # Remoeves bad text from chapters.
+        for content in body_parts.select("p"):
+            for bad in ["Prev", "ToC", "Next"]:
+                if bad in content.text:
+                    content.decompose()
+
+        # Fixes images, so they can be downloaded.
+        all_imgs = soup.find_all('img')
+        for img in all_imgs:
+            if img.has_attr('data-orig-file'):
+                src_url = img['src']
+                parent = img.parent
+                img.decompose()
+                new_tag = soup.new_tag("img", src=src_url)
+                parent.append(new_tag)
+
+        return str(body_parts)
+    # end def
+# end class
\ No newline at end of file
diff --git a/lncrawl/sources/dsrealmtrans.py b/lncrawl/sources/dsrealmtrans.py
index c47dc9ec8..ff072d169 100644
--- a/lncrawl/sources/dsrealmtrans.py
+++ b/lncrawl/sources/dsrealmtrans.py
@@ -56,8 +56,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         body = []
         contents = soup.select('div.wpb_wrapper p')
         for p in contents:
diff --git a/lncrawl/sources/esmtlnovel.py b/lncrawl/sources/esmtlnovel.py
index 38a06cc31..a7969ba06 100644
--- a/lncrawl/sources/esmtlnovel.py
+++ b/lncrawl/sources/esmtlnovel.py
@@ -90,8 +90,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         contents = soup.select('div.par p')
         # print(contents)
         # for p in contents:
diff --git a/lncrawl/sources/exiledrebels.py b/lncrawl/sources/exiledrebels.py
index 7f4502500..34b1debed 100644
--- a/lncrawl/sources/exiledrebels.py
+++ b/lncrawl/sources/exiledrebels.py
@@ -54,8 +54,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         body = []
         contents = soup.select('div#wtr-content p')
         for p in contents:
diff --git a/lncrawl/sources/foxaholic.py b/lncrawl/sources/foxaholic.py
index c8208e921..f8997e2d6 100644
--- a/lncrawl/sources/foxaholic.py
+++ b/lncrawl/sources/foxaholic.py
@@ -2,14 +2,17 @@
 import json
 import logging
 import re
-
+from urllib.parse import urlparse
 from ..utils.crawler import Crawler
+from ..utils.cleaner import cleanup_text
 
 logger = logging.getLogger(__name__)
-search_url = 'https://foxaholic.com/?s=%s&post_type=wp-manga'
+search_url = 'https://www.foxaholic.com/?s=%s&post_type=wp-manga'
+chapter_list_url = 'https://www.foxaholic.com/wp-admin/admin-ajax.php'
+
 
 class FoxaholicCrawler(Crawler):
-    base_url = 'https://foxaholic.com/'
+    base_url = 'https://www.foxaholic.com/'
 
     def search_novel(self, query):
         query = query.lower().replace(' ', '+')
@@ -19,10 +22,11 @@ def search_novel(self, query):
         for tab in soup.select('.c-tabs-item__content'):
             a = tab.select_one('.post-title h3 a')
             latest = tab.select_one('.latest-chap .chapter a').text
+            status = tab.select_one('.mg_release .summary-content a')
             results.append({
                 'title': a.text.strip(),
                 'url': self.absolute_url(a['href']),
-                'info': 'Latest: %s' % latest,
+                'info': '%s | Status: %s' % (latest, status.text.strip()),
             })
         # end for
 
@@ -34,76 +38,55 @@ def read_novel_info(self):
         logger.debug('Visiting %s', self.novel_url)
         soup = self.get_soup(self.novel_url)
 
-        self.novel_title = ' '.join([
-            str(x)
-            for x in soup.select_one('.post-title h1, .post-title h3').contents
-            if not x.name
-        ]).strip()
+        self.novel_title = soup.select_one(
+            'meta[property="og:title"]')['content']
         logger.info('Novel title: %s', self.novel_title)
 
         self.novel_cover = self.absolute_url(
-            soup.select_one('.summary_image img')['data-src'])
+            soup.select_one('.summary_image a img')['data-src'])
         logger.info('Novel cover: %s', self.novel_cover)
 
-        author = soup.find('div', {'class': 'author-content'}).findAll('a')
-        if len(author) == 2:
-            self.novel_author = author[0].text + ' (' + author[1].text + ')'
-        else:
-            self.novel_author = author[0].text
-        logger.info('Novel author: %s', self.novel_author)
-
-        data_id = soup.select_one('#manga-chapters-holder')['data-id']
-        chapters_response = self.submit_form("https://foxaholic.com/wp-admin/admin-ajax.php",{"action":"manga_get_chapters","manga":data_id})
-        chapter_soup = self.make_soup(chapters_response,None)
-        content_area = chapter_soup.select_one(' .page-content-listing')
+        self.novel_author = ' '.join([
+            a.text.strip()
+            for a in soup.select('.author-content a[href*="novel-author"]')
+        ])
+        logger.info('%s', self.novel_author)
 
-        for span in content_area.findAll('span'):
-            span.decompose()
+        self.novel_id = soup.select_one('#manga-chapters-holder')['data-id']
+        logger.info('Novel id: %s', self.novel_id)
 
-        chapters = content_area.select('ul.main li.wp-manga-chapter a')
-
-        chapters.reverse()
-
-        for a in chapters:
+        response = self.submit_form(
+            chapter_list_url, data='action=manga_get_chapters&manga=' + self.novel_id)
+        soup = self.make_soup(response)
+        for a in reversed(soup.select('.wp-manga-chapter a')):
             chap_id = len(self.chapters) + 1
-            vol_id = chap_id//100 + 1
-            if len(self.chapters) % 100 == 0:
-                vol_title = 'Volume ' + str(vol_id)
-                self.volumes.append({
-                    'id': vol_id,
-                    'title': vol_title,
-                })
+            vol_id = 1 + len(self.chapters) // 100
+            if chap_id % 100 == 1:
+                self.volumes.append({'id': vol_id})
             # end if
             self.chapters.append({
                 'id': chap_id,
                 'volume': vol_id,
+                'title': a.text.strip(),
                 'url':  self.absolute_url(a['href']),
-                'title': a.text.strip() or ('Chapter %d' % chap_id),
             })
         # end for
     # end def
 
+    @cleanup_text
     def download_chapter_body(self, chapter):
         '''Download body of a single chapter and return as clean html format.'''
-        logger.info('Downloading %s', chapter['url'])
+        logger.info('Visiting %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
-
-        contents = soup.select_one('div.text-left')
-
-        for img in contents.findAll('img'):
-            if img.has_attr('data-lazy-src'):
-                src_url = img['data-lazy-src']
+        contents = soup.select('.reading-content p') 
+        all_imgs = soup.find_all('img')
+        for img in all_imgs:
+            if img.has_attr('loading'):
+                src_url = img['src']
                 parent = img.parent
                 img.decompose()
                 new_tag = soup.new_tag("img", src=src_url)
                 parent.append(new_tag)
-
-        if contents.h3:
-            contents.h3.decompose()
-
-        for codeblock in contents.findAll('div', {'class': 'code-block'}):
-            codeblock.decompose()
-
-        return str(contents)
+        return ''.join([str(p) for p in contents])
     # end def
-# end class
+# end class
\ No newline at end of file
diff --git a/lncrawl/sources/frmtlnovel.py b/lncrawl/sources/frmtlnovel.py
index 45b76beee..b85d3eaa9 100644
--- a/lncrawl/sources/frmtlnovel.py
+++ b/lncrawl/sources/frmtlnovel.py
@@ -90,8 +90,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         contents = soup.select('div.par p')
         # print(contents)
         # for p in contents:
diff --git a/lncrawl/sources/fu_kemao.py b/lncrawl/sources/fu_kemao.py
index 34411674e..5e472a06d 100644
--- a/lncrawl/sources/fu_kemao.py
+++ b/lncrawl/sources/fu_kemao.py
@@ -35,12 +35,12 @@ def read_novel_info(self):
         # logger.debug('Visiting %s', self.novel_url)
         soup = self.get_soup(self.novel_url)
 
-        self.novel_title = soup.select_one(
-            '#novel-info h5, #recentnovels h5').text.strip()
-        logger.info('Novel title: %s', self.novel_title)
+        title = soup.select_one('title').text
+        self.novel_title = title.rsplit('–', 1)[0].strip()
+        logger.debug('Novel title = %s', self.novel_title)
 
         self.novel_cover = self.absolute_url(
-            soup.select_one('#novel-info amp-img, #recentnovels amp-img')['src'])
+            soup.select_one('#thumbnail img')['src'])
         logger.info('Novel cover: %s', self.novel_cover)
 
         # self.novel_author = soup.select_one('#Publisher a')['href']
@@ -56,7 +56,7 @@ def read_novel_info(self):
 
         def parse_chapter_list(soup):
             temp_list = []
-            for a in soup.select('#chapterList td a'):
+            for a in soup.select('.chapter-list td a'):
                 temp_list.append({
                     'title': a.text.strip(),
                     'url': self.absolute_url(a['href']),
@@ -111,13 +111,12 @@ def download_chapter_body(self, chapter):
         soup = self.get_soup(chapter['url'])
         # logger.debug(soup.title.string)
 
-        body = soup.select_one("#content, article")
+        body = soup.select_one(".entry-content")
 
-        paragraphs = []
-        for p in body.select('p'):
-            if 'class' not in p.attrs and p.text.strip():
-                paragraphs.append(str(p))
+        # Removes "junk" from chapters.
+        for share in body.select('script, hr, br, #div-gpt-ad-comrademaocom35917, #div-gpt-ad-comrademaocom35918'):
+            share.decompose()
 
-        return ''.join(paragraphs)
+        return str(body)
     # end def
 # end class
diff --git a/lncrawl/sources/hui3r.py b/lncrawl/sources/hui3r.py
index c3aa6718d..dface91ca 100644
--- a/lncrawl/sources/hui3r.py
+++ b/lncrawl/sources/hui3r.py
@@ -19,16 +19,20 @@ def read_novel_info(self):
         self.novel_title = soup.select_one('.single-entry-content h3 a').text.strip()
         logger.info('Novel title: %s', self.novel_title)
 
-        # NOTE: Having trouble grabbing cover without error message.
+        # TODO: Having trouble grabbing cover without error message (cannot identify image file <_io.BytesIO object at 0x000002CC03335F40>).
         # self.novel_cover = self.absolute_url(
         #     soup.select_one('.single-entry-content p img')['src'])
         # logger.info('Novel cover: %s', self.novel_cover)
 
-        self.novel_author = "by hui3r"
+        self.novel_author = "Translated by hui3r"
         logger.info('Novel author: %s', self.novel_author)
 
+        # Removes none TOC links from bottom of page.
+        toc_parts = soup.select_one('.single-entry-content')
+        for notoc in toc_parts.select('.sharedaddy, .inline-ad-slot, .code-block, script, .adsbygoogle'):
+            notoc.decompose()
+
         # Extract volume-wise chapter entries
-        # NOTE: Addd /2 to end of url, so it only grabs chapters instead of social media links as well.
         chapters = soup.select('.single-entry-content ul li a[href*="hui3r.wordpress.com/2"]')
 
         for a in chapters:
@@ -54,7 +58,6 @@ def download_chapter_body(self, chapter):
         '''Download body of a single chapter and return as clean html format.'''
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
-        logger.debug(soup.title.string)
 
         body_parts = soup.select_one('.single-entry-content')
 
diff --git a/lncrawl/sources/idmtl.py b/lncrawl/sources/idmtl.py
index 75246a85a..f3fdc516e 100644
--- a/lncrawl/sources/idmtl.py
+++ b/lncrawl/sources/idmtl.py
@@ -73,14 +73,9 @@ def read_novel_info(self):
                 'url': item['permalink'],
             })
         # end for
-        logger.debug(self.chapters)
 
         self.volumes = [{'id': x + 1}
                         for x in range(len(self.chapters) // 100 + 1)]
-        logger.debug(self.volumes)
-
-        logger.debug('%d volumes & %d chapters found',
-                     len(self.volumes), len(self.chapters))
     # end def
 
     def download_chapter_body(self, chapter):
diff --git a/lncrawl/sources/idmtlnovel.py b/lncrawl/sources/idmtlnovel.py
index b1bd15db1..22b2f8dfb 100644
--- a/lncrawl/sources/idmtlnovel.py
+++ b/lncrawl/sources/idmtlnovel.py
@@ -77,8 +77,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         contents = soup.select('div.par p')
         # print(contents)
         # for p in contents:
diff --git a/lncrawl/sources/inadequatetrans.py b/lncrawl/sources/inadequatetrans.py
index 06448f2c8..da9995884 100644
--- a/lncrawl/sources/inadequatetrans.py
+++ b/lncrawl/sources/inadequatetrans.py
@@ -18,7 +18,7 @@ def read_novel_info(self):
         self.novel_title = soup.find("a", {"aria-current": "page"}).text.strip()
         logger.info('Novel title: %s', self.novel_title)
 
-        # NOTE: Site list no cover images.
+        # TODO: Site list no cover images.
         # self.novel_cover = self.absolute_url(
         #     soup.select_one('div.entry-content p img')['src'])
         # logger.info('Novel cover: %s', self.novel_cover)
@@ -26,8 +26,12 @@ def read_novel_info(self):
         self.novel_author = "by Inadequate Translations"
         logger.info('Novel author: %s', self.novel_author)
 
+        # Removes none TOC links from bottom of page.
+        toc_parts = soup.select_one('.entry-content')
+        for notoc in toc_parts.select('.sharedaddy'):
+            notoc.decompose()
+
         # Extract volume-wise chapter entries
-        # FIXME: Sometimes grabs social media link at bottom of page, No idea how to exclude links.
         chapters = soup.select('.entry-content a[href*="inadequatetranslations.wordpress.com"]')
 
         for a in chapters:
@@ -54,8 +58,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         body = []
         contents = soup.select('div.entry-content p')
         for p in contents:
diff --git a/lncrawl/sources/indomtl.py b/lncrawl/sources/indomtl.py
index 75246a85a..f3fdc516e 100644
--- a/lncrawl/sources/indomtl.py
+++ b/lncrawl/sources/indomtl.py
@@ -73,14 +73,9 @@ def read_novel_info(self):
                 'url': item['permalink'],
             })
         # end for
-        logger.debug(self.chapters)
 
         self.volumes = [{'id': x + 1}
                         for x in range(len(self.chapters) // 100 + 1)]
-        logger.debug(self.volumes)
-
-        logger.debug('%d volumes & %d chapters found',
-                     len(self.volumes), len(self.chapters))
     # end def
 
     def download_chapter_body(self, chapter):
diff --git a/lncrawl/sources/infinitetrans.py b/lncrawl/sources/infinitetrans.py
index 5ddf02fe4..29f57fbd6 100644
--- a/lncrawl/sources/infinitetrans.py
+++ b/lncrawl/sources/infinitetrans.py
@@ -53,8 +53,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         body_parts = soup.select_one('div.entry-content')
 
         # Remoeves Nav Button from top and bottom of chapters.
diff --git a/lncrawl/sources/isotls.py b/lncrawl/sources/isotls.py
new file mode 100644
index 000000000..478cf3e67
--- /dev/null
+++ b/lncrawl/sources/isotls.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+import logging
+from ..utils.crawler import Crawler
+
+logger = logging.getLogger(__name__)
+
+class IsotlsCrawler(Crawler):
+    base_url = 'https://isotls.com/'
+
+    def read_novel_info(self):
+        '''Get novel title, autor, cover etc'''
+        logger.debug('Visiting %s', self.novel_url)
+        soup = self.get_soup(self.novel_url)
+
+        self.novel_cover = soup.select_one(
+            'meta[property="og:image"]')['content']
+        logger.info('Novel cover: %s', self.novel_cover)
+
+        self.novel_title = soup.select_one(
+            'meta[property="og:title"]')['content']
+        logger.info('Novel title: %s', self.novel_title)
+
+        self.novel_author = soup.select_one(
+            'meta[name="twitter:data1"]')['content']
+        logger.info('%s', self.novel_author)
+
+        for a in soup.select('main section div:nth-child(2) ul li a'):
+            chap_id = len(self.chapters) + 1
+            vol_id = len(self.chapters) // 100 + 1
+            if len(self.chapters) % 100 == 0:
+                self.volumes.append({'id': vol_id})
+            # end if
+            self.chapters.append({
+                'id': chap_id,
+                'volume': vol_id,
+                'title': a.text.strip(),
+                'url': self.absolute_url(a['href']),
+            })
+        # end for
+    # end def
+
+    def download_chapter_body(self, chapter):
+        '''Download body of a single chapter and return as clean html format.'''
+        logger.info('Downloading %s', chapter['url'])
+        soup = self.get_soup(chapter['url'])
+        contents = soup.select('article p')
+        body = [str(p) for p in contents if p.text.strip()]
+        return '<p>' + '</p><p>'.join(body) + '</p>'
+    # end def
+# end class
\ No newline at end of file
diff --git a/lncrawl/sources/justatrans.py b/lncrawl/sources/justatrans.py
index 7f6d7d226..fee93bbb1 100644
--- a/lncrawl/sources/justatrans.py
+++ b/lncrawl/sources/justatrans.py
@@ -54,8 +54,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         body_parts = soup.select_one('div.entry-content')
 
         # Removes "Share this" text and buttons from bottom of chapters.
diff --git a/lncrawl/sources/ladybirdtrans.py b/lncrawl/sources/ladybirdtrans.py
new file mode 100644
index 000000000..af5128656
--- /dev/null
+++ b/lncrawl/sources/ladybirdtrans.py
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+import json
+import logging
+import re
+
+from ..utils.crawler import Crawler
+
+logger = logging.getLogger(__name__)
+
+class lazybirdtranslations(Crawler):
+    base_url = 'https://lazybirdtranslations.wordpress.com/'
+
+    def read_novel_info(self):
+        '''Get novel title, autor, cover etc'''
+        logger.debug('Visiting %s', self.novel_url)
+        soup = self.get_soup(self.novel_url)
+
+        self.novel_cover = soup.select_one(
+            'meta[property="og:image"]')['content']
+        logger.info('Novel cover: %s', self.novel_cover)
+
+        self.novel_title = soup.select_one(
+            'meta[property="og:title"]')['content']
+        logger.info('Novel title: %s', self.novel_title)
+
+        authors = []
+        for a in soup.select('figcaption'):
+                authors.append(a.text.strip())
+            # end if
+        # end for
+        self.novel_author = ', '.join(authors)
+        logger.info('Novel author: %s', self.novel_author)
+
+        # Stops external links being selected as chapters
+        chapters = soup.select('.wp-block-table tr td a[href*="lazybirdtranslations.wordpress.com/2"]')
+
+        for a in chapters:
+            chap_id = len(self.chapters) + 1
+            if len(self.chapters) % 100 == 0:
+                vol_id = chap_id//100 + 1
+                vol_title = 'Volume ' + str(vol_id)
+                self.volumes.append({
+                    'id': vol_id,
+                    'title': vol_title,
+                })
+            # end if
+            self.chapters.append({
+                'id': chap_id,
+                'volume': vol_id,
+                'url':  self.absolute_url(a['href']),
+                'title': a.text.strip() or ('Chapter %d' % chap_id),
+            })
+        # end for
+    # end def
+
+    def download_chapter_body(self, chapter):
+        '''Download body of a single chapter and return as clean html format.'''
+        logger.info('Downloading %s', chapter['url'])
+        soup = self.get_soup(chapter['url'])
+
+        # Adds proper chapter ttile, only problem is some are in 2 parts so there named the same.
+        if 'Chapter' in soup.select_one('h2').text:
+            chapter['title'] = soup.select_one('h2').text
+        else:
+            chapter['title'] = chapter['title']
+        # end if
+
+        body_parts = soup.select_one('div.entry-content')
+
+        for content in body_parts.select("p"):
+            for bad in ["[Index]", "[Previous]", "[Next]"]:
+                if bad in content.text:
+                    content.decompose()
+
+        for bad in body_parts.select('br, .entry-meta, .inline-ad-slot, .has-text-align-center, #jp-post-flair, .entry-footer'):
+            bad.decompose()
+
+        body = self.extract_contents(body_parts)
+        return '<p>' + '</p><p>'.join(body) + '</p>'
+    # end def
+# end class
\ No newline at end of file
diff --git a/lncrawl/sources/lemontree.py b/lncrawl/sources/lemontree.py
index 667434a93..1dd37f893 100644
--- a/lncrawl/sources/lemontree.py
+++ b/lncrawl/sources/lemontree.py
@@ -18,7 +18,7 @@ def read_novel_info(self):
         self.novel_title = soup.find("h1", {"class": "entry-title"}).text.strip()
         logger.info('Novel title: %s', self.novel_title)
 
-        # NOTE: Site list no cover images.
+        # TODO: Site list no cover images.
         # self.novel_cover = self.absolute_url(
         #     soup.select_one('div.entry-content p img')['src'])
         # logger.info('Novel cover: %s', self.novel_cover)
@@ -26,8 +26,12 @@ def read_novel_info(self):
         self.novel_author = "by Lemon Tree Translations"
         logger.info('Novel author: %s', self.novel_author)
 
+        # Removes none TOC links from bottom of page.
+        toc_parts = soup.select_one('.entry-content')
+        for notoc in toc_parts.select('.sharedaddy'):
+            notoc.decompose()
+
         # Extract volume-wise chapter entries
-        # FIXME: Sometimes grabs social media link at bottom of page, No idea how to exclude links.
         chapters = soup.select('div.entry-content ul li [href*="lemontreetranslations"]')
 
         for a in chapters:
@@ -54,8 +58,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         body = []
         contents = soup.select('div.entry-content p')
         for p in contents:
diff --git a/lncrawl/sources/lightnovelkiss.py b/lncrawl/sources/lightnovelkiss.py
index ad5804dcb..a97643949 100644
--- a/lncrawl/sources/lightnovelkiss.py
+++ b/lncrawl/sources/lightnovelkiss.py
@@ -38,7 +38,11 @@ def read_novel_info(self):
         logger.debug('Visiting %s', self.novel_url)
         soup = self.get_soup(self.novel_url)
 
-        # NOTE: Title has "Novel" at the end of book title, can't seem to remove it.
+        # NOTE: Title has "Novel" at the end of book title, this code should work but can't test at moment because site is having problems.
+        # title = soup.select_one('.post-title h1').text
+        # self.novel_title = title.rsplit(' ', 1)[0].strip()
+        # logger.debug('Novel title = %s', self.novel_title)
+
         possible_title = soup.select_one('.post-title h1')
         for span in possible_title.select('span'):
             span.extract()
diff --git a/lncrawl/sources/lightnovelpub.py b/lncrawl/sources/lightnovelpub.py
index 22ff148e4..015c3730b 100644
--- a/lncrawl/sources/lightnovelpub.py
+++ b/lncrawl/sources/lightnovelpub.py
@@ -32,6 +32,8 @@ def search_novel(self, query):
 
     def read_novel_info(self):
         '''Get novel title, autor, cover etc'''
+        self.novel_url = '%s%s' % (self.base_url, urlparse(self.novel_url).path)
+
         logger.debug('Visiting %s', self.novel_url)
         soup = self.get_soup(self.novel_url)
 
diff --git a/lncrawl/sources/lightnovelshub.py b/lncrawl/sources/lightnovelshub.py
new file mode 100644
index 000000000..32754705d
--- /dev/null
+++ b/lncrawl/sources/lightnovelshub.py
@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+import logging
+from ..utils.crawler import Crawler
+
+logger = logging.getLogger(__name__)
+search_url = 'https://lightnovelshub.com/?s=%s&post_type=wp-manga&author=&artist=&release='
+chapter_list_url = 'https://lightnovelshub.com/wp-admin/admin-ajax.php'
+
+
+class LightNovelsHubCrawler(Crawler):
+    base_url = 'https://lightnovelshub.com/'
+
+    def search_novel(self, query):
+        query = query.lower().replace(' ', '+')
+        soup = self.get_soup(search_url % query)
+
+        results = []
+        for tab in soup.select('.c-tabs-item__content'):
+            a = tab.select_one('.post-title h3 a')
+            latest = tab.select_one('.latest-chap .chapter a').text
+            votes = tab.select_one('.rating .total_votes').text
+            results.append({
+                'title': a.text.strip(),
+                'url': self.absolute_url(a['href']),
+                'info': '%s | Rating: %s' % (latest, votes),
+            })
+        # end for
+
+        return results
+    # end def
+
+    def read_novel_info(self):
+        '''Get novel title, autor, cover etc'''
+        logger.debug('Visiting %s', self.novel_url)
+        soup = self.get_soup(self.novel_url)
+
+        possible_title = soup.select_one('.post-title h1')
+        for span in possible_title.select('span'):
+            span.extract()
+        # end for
+        self.novel_title = possible_title.text.strip()
+        logger.info('Novel title: %s', self.novel_title)
+
+        self.novel_cover = self.absolute_url(
+            soup.select_one('.summary_image a img')['src'])
+        logger.info('Novel cover: %s', self.novel_cover)
+
+        self.novel_author = ' '.join([
+            a.text.strip()
+            for a in soup.select('.author-content a[href*="manga-author"]')
+        ])
+        logger.info('%s', self.novel_author)
+
+        self.novel_id = soup.select_one('#manga-chapters-holder')['data-id']
+        logger.info('Novel id: %s', self.novel_id)
+
+        response = self.submit_form(
+            chapter_list_url, data='action=manga_get_chapters&manga=' + self.novel_id)
+        soup = self.make_soup(response)
+        for a in reversed(soup.select('.wp-manga-chapter a')):
+            chap_id = len(self.chapters) + 1
+            vol_id = 1 + len(self.chapters) // 100
+            if chap_id % 100 == 1:
+                self.volumes.append({'id': vol_id})
+            # end if
+            self.chapters.append({
+                'id': chap_id,
+                'volume': vol_id,
+                'title': a.text.strip(),
+                'url':  self.absolute_url(a['href']),
+            })
+        # end for
+    # end def
+
+    def download_chapter_body(self, chapter):
+        '''Download body of a single chapter and return as clean html format.'''
+        logger.info('Visiting %s', chapter['url'])
+        soup = self.get_soup(chapter['url'])
+        contents = soup.select('.reading-content p')
+        return ''.join([str(p) for p in contents])
+    # end def
+# end class
\ No newline at end of file
diff --git a/lncrawl/sources/lightnovelsonl.py b/lncrawl/sources/lightnovelsonl.py
index 36bf28025..26dc15d60 100644
--- a/lncrawl/sources/lightnovelsonl.py
+++ b/lncrawl/sources/lightnovelsonl.py
@@ -69,8 +69,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         if 'Chapter' in soup.select_one('h1').text:
             chapter['title'] = soup.select_one('h1').text
         else:
diff --git a/lncrawl/sources/lightnovelworld.py b/lncrawl/sources/lightnovelworld.py
index b77a0b63e..4a32e418a 100644
--- a/lncrawl/sources/lightnovelworld.py
+++ b/lncrawl/sources/lightnovelworld.py
@@ -15,6 +15,9 @@ def read_novel_info(self):
         logger.debug('Visiting %s', self.novel_url)
         soup = self.get_soup(self.novel_url)
 
+        self.novel_author = soup.select_one('span.textC999').text.strip()
+        logger.info('Novel author: %s', self.novel_author)
+
         possible_title = soup.select_one('li.text1')
         for span in possible_title.select('span'):
             span.extract()
@@ -26,10 +29,6 @@ def read_novel_info(self):
             soup.select_one('.book_info_l img')['src'])
         logger.info('Novel cover: %s', self.novel_cover)
 
-        # NOTE: Could not get author name from page, kept getting ResultSet object has no attribute 'text'.
-        # self.novel_author = soup.select('h1 span').text.strip()
-        # logger.info('Novel author: %s', self.novel_author)
-
         volumes = set([])
         for a in soup.select('div#chapter_content ul li a'):
             chap_id = 1 + len(self.chapters)
diff --git a/lncrawl/sources/lightnovetrans.py b/lncrawl/sources/lightnovetrans.py
index 9fa48fb62..baebbd08e 100644
--- a/lncrawl/sources/lightnovetrans.py
+++ b/lncrawl/sources/lightnovetrans.py
@@ -1,72 +1,55 @@
 # -*- coding: utf-8 -*-
-import json
 import logging
-import re
 from ..utils.crawler import Crawler
 
 logger = logging.getLogger(__name__)
 
+
 class LNTCrawler(Crawler):
     base_url = 'https://lightnovelstranslations.com/'
 
-    def __parse_toc(self, soup):
-        '''parse and return the toc list'''
-
-        volumes = []
-        chapters = []
-
-        for div in soup.select('div.su-accordion'):
-            vol = div.select('div.su-spoiler-title strong')[1].text.strip()
-            vol_id = int(vol) if vol.isdigit() else len(volumes) + 1
-            volumes.append(
-                {
-                    'id': vol_id,
-                    'title': vol,
-                }
-            )
-            for a in div.select('.su-spoiler-content p a'):
-                chapters.append(
-                    {
-                        'id': len(chapters) + 1,
-                        'volume': vol_id,
-                        'title': a.text.strip(),
-                        'url': self.absolute_url(a['href']),
-                    }
-                )
-            # end for
-        # end for
-        return (volumes, chapters)
-
     def read_novel_info(self):
         '''Get novel title, autor, cover etc'''
         logger.debug('Visiting %s', self.novel_url)
+
         soup = self.get_soup(self.novel_url)
 
         self.novel_title = soup.select_one('h1.entry-title').text
         logger.info('Novel title: %s', self.novel_title)
 
-        # NOTE: No covers on site, could not grab author name.
+        # TODO: No covers on site, could not grab author name.
 
         # Extract volume-wise chapter entries
-        # chapter_urls = set([])
-        self.volumes, self.chapters = self.__parse_toc(soup)
-
+        for div in soup.select('.su-accordion .su-spoiler'):
+            vol = div.select_one('.su-spoiler-title').text.strip()
+            vol_id = int(vol) if vol.isdigit() else len(self.volumes) + 1
+            self.volumes.append({
+                'id': vol_id,
+                'title': vol,
+            })
+            for a in div.select('.su-spoiler-content p a'):
+                self.chapters.append({
+                    'id': len(self.chapters) + 1,
+                    'volume': vol_id,
+                    'title': a.text.strip(),
+                    'url': self.absolute_url(a['href']),
+                })
+            # end for
+        # end for
     # end def
 
     def download_chapter_body(self, chapter):
         logger.info('Visiting: %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
-        content = soup.select_one('.entry-content')
 
-        for bad in content.select(
-            '.alignleft, .alignright, hr, p[style*="text-align: center"]'
-        ):
+        content = soup.select_one('.entry-content')
+        for bad in content.select('.alignleft, .alignright, hr, p[style*="text-align: center"]'):
             bad.decompose()
         # end for
 
-        return str(content)
+        return '\n'.join([str(p) for p in content.find_all('p')])
 
     # end def
 
 
-# end class
\ No newline at end of file
+# end class
diff --git a/lncrawl/sources/mangatoon.py b/lncrawl/sources/mangatoon.py
index 69a8061c1..b5be05da4 100644
--- a/lncrawl/sources/mangatoon.py
+++ b/lncrawl/sources/mangatoon.py
@@ -12,7 +12,9 @@
 
 
 class MangatoonMobiCrawler(Crawler):
-    base_url = 'https://mangatoon.mobi/'
+    base_url = [
+        'https://mangatoon.mobi/',
+    ]
 
     def initialize(self):
         self.home_url = 'https://mangatoon.mobi'
@@ -70,6 +72,7 @@ def download_chapter_body(self, chapter):
 
         chapter_content = ast.literal_eval(content)
         chapter_content = [p.replace(r'\-', '-') for p in chapter_content]
+        chapter_content = [p.replace('\\', '') for p in chapter_content]
         return '<p>' + '</p><p>'.join(chapter_content) + '</p>'
     # end def
 # end class
diff --git a/lncrawl/sources/meionovel.py b/lncrawl/sources/meionovel.py
index 4f94ca58d..02cd1b937 100644
--- a/lncrawl/sources/meionovel.py
+++ b/lncrawl/sources/meionovel.py
@@ -7,6 +7,8 @@
 
 logger = logging.getLogger(__name__)
 search_url = 'https://meionovel.id/?s=%s&post_type=wp-manga&author=&artist=&release='
+chapter_list_url = 'https://meionovel.id/wp-admin/admin-ajax.php'
+
 
 class MeionovelCrawler(Crawler):
     base_url = 'https://meionovel.id/'
@@ -36,50 +38,42 @@ def read_novel_info(self):
         logger.debug('Visiting %s', self.novel_url)
         soup = self.get_soup(self.novel_url)
 
-        self.novel_title = ' '.join([
-            str(x)
-            for x in soup.select_one('.post-title h3').contents
-            if not x.name
-        ]).strip()
+        self.novel_title = soup.select_one('meta[property="og:title"]')['content'].split('-')[0]
         logger.info('Novel title: %s', self.novel_title)
 
-        self.novel_cover = self.absolute_url(
-            soup.select_one('.summary_image img')['data-src'])
+        self.novel_cover = self.absolute_url(soup.select_one('.summary_image img')['data-src'])
         logger.info('Novel cover: %s', self.novel_cover)
 
-        author = soup.find('div', {'class': 'author-content'}).findAll('a')
-        if len(author) == 2:
-            self.novel_author = author[0].text + ' (' + author[1].text + ')'
-        else:
-            self.novel_author = author[0].text
+        possible_authors = [a.text.strip() for a in soup.select('.author-content a')]
+        self.novel_author = ', '.join(filter(None, possible_authors))
         logger.info('Novel author: %s', self.novel_author)
 
-        content_area = soup.select_one(' .page-content-listing')
-
-        for span in content_area.findAll('span'):
-            span.decompose()
+        manga_id = soup.select_one('#manga-chapters-holder')['data-id']
+        logger.info('Manga id: %s', manga_id)
 
-        chapters = content_area.select('ul.main li.wp-manga-chapter a')
+        soup = self.post_soup(chapter_list_url, {
+            'action': 'manga_get_chapters',
+            'manga': manga_id,
+        }, headers={
+            'Origin': self.base_url,
+            'Referer': self.novel_url,
+            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
+        })
 
-        chapters.reverse()
-
-        for a in chapters:
+        vols = set([])
+        for a in reversed(soup.select('.wp-manga-chapter a')):
             chap_id = len(self.chapters) + 1
-            vol_id = chap_id//100 + 1
-            if len(self.chapters) % 100 == 0:
-                vol_title = 'Volume ' + str(vol_id)
-                self.volumes.append({
-                    'id': vol_id,
-                    'title': vol_title,
-                })
-            # end if
+            vol_id = chap_id // 100 + 1
+            vols.add(vol_id)
             self.chapters.append({
                 'id': chap_id,
                 'volume': vol_id,
+                'title': a.text.strip(),
                 'url':  self.absolute_url(a['href']),
-                'title': a.text.strip() or ('Chapter %d' % chap_id),
             })
         # end for
+
+        self.volumes = [{'id': x} for x in vols]
     # end def
 
     def download_chapter_body(self, chapter):
diff --git a/lncrawl/sources/mtlednovels.py b/lncrawl/sources/mtlednovels.py
index 3bb378436..d38088b4d 100644
--- a/lncrawl/sources/mtlednovels.py
+++ b/lncrawl/sources/mtlednovels.py
@@ -51,22 +51,21 @@ def logout(self):
         print('Logged out')
     # end def
 
-    # NOTE: Disabled search for cloudflare issue
-    # def search_novel(self, query):
-    #     query = query.lower().replace(' ', '+')
-    #     soup = self.get_soup(search_url % query)
-
-    #     results = []
-    #     for a in soup.select('.card .row .col-lg-2 a')[:5]:
-    #         url = self.absolute_url(a['href'])
-    #         results.append({
-    #             'url': url,
-    #             'title': a.img['alt'],
-    #         })
-    #     # end for
-
-    #     return results
-    # # end def
+    def search_novel(self, query):
+        query = query.lower().replace(' ', '+')
+        soup = self.get_soup(search_url % query)
+
+        results = []
+        for a in soup.select('.card .row .col-lg-2 a')[:5]:
+            url = self.absolute_url(a['href'])
+            results.append({
+                'url': url,
+                'title': a.img['alt'],
+            })
+        # end for
+
+        return results
+    # end def
 
     def read_novel_info(self):
         '''Get novel title, autor, cover etc'''
@@ -109,8 +108,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         if soup.h1.text.strip():
             chapter['title'] = soup.h1.text.strip()
         else:
diff --git a/lncrawl/sources/mtlnovel.py b/lncrawl/sources/mtlnovel.py
index cd1fc7771..27a4d6539 100644
--- a/lncrawl/sources/mtlnovel.py
+++ b/lncrawl/sources/mtlnovel.py
@@ -78,8 +78,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         contents = soup.select('div.par p')
         # print(contents)
         # for p in contents:
diff --git a/lncrawl/sources/novelfull.py b/lncrawl/sources/novelfull.py
index d66ab36b1..4a5e7e233 100644
--- a/lncrawl/sources/novelfull.py
+++ b/lncrawl/sources/novelfull.py
@@ -124,9 +124,14 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        content = soup.select('div#chapter-content p')
-        content = '\n'.join(str(p) for p in content)
+        contents = soup.select_one('div#chapter-content')
 
-        return content
+        for bad in contents.select('iframe, .ads-middle, .code-block, script, .adsbygoogle, img[src*="proxy?container=focus"]'):
+            bad.decompose()
+
+        for end in contents.findAll('div', {"align": 'left'}):
+            end.decompose()
+
+        return str(contents)
     # end def
 # end class
diff --git a/lncrawl/sources/novelgo.py b/lncrawl/sources/novelgo.py
index 8924058b9..eb70568cf 100644
--- a/lncrawl/sources/novelgo.py
+++ b/lncrawl/sources/novelgo.py
@@ -94,8 +94,6 @@ def read_novel_info(self):
             })
             # end for
         # end for
-
-        logger.debug(self.chapters)
     # end def
 
     def download_chapter_body(self, chapter):
diff --git a/lncrawl/sources/novelhunters.py b/lncrawl/sources/novelhunters.py
index c02f1ef8f..d3e740458 100644
--- a/lncrawl/sources/novelhunters.py
+++ b/lncrawl/sources/novelhunters.py
@@ -16,7 +16,6 @@ class NovelHunters(Crawler):
     base_url = 'https://www.novelhunters.com/'
 
     def initialize(self):
-        print('inititiaslkaksjdlkasjdlkajsdlkjdls')
         self.executor = ThreadPoolExecutor(max_workers=7)
     # end def
 
diff --git a/lncrawl/sources/novelmic.py b/lncrawl/sources/novelmic.py
new file mode 100644
index 000000000..856d3182f
--- /dev/null
+++ b/lncrawl/sources/novelmic.py
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+import json
+import logging
+import re
+from urllib.parse import urlparse
+from ..utils.cleaner import cleanup_text
+from ..utils.crawler import Crawler
+
+logger = logging.getLogger(__name__)
+search_url = 'https://novelmic.com/?s=%s&post_type=wp-manga'
+chapter_list_url = 'https://novelmic.com/wp-admin/admin-ajax.php'
+
+
+class NovelMic(Crawler):
+    base_url = 'https://novelmic.com/'
+
+    # TODO: Search not working, not sure why/
+    # def search_novel(self, query):
+    #     query = query.lower().replace(' ', '+')
+    #     soup = self.get_soup(search_url % query)
+
+    #     results = []
+    #     for tab in soup.select('.c-tabs-item__content'):
+    #         a = tab.select_one('.post-title h3 a')
+    #         latest = tab.select_one('.latest-chap .chapter a').text
+    #         votes = tab.select_one('.rating .total_votes').text
+    #         results.append({
+    #             'title': a.text.strip(),
+    #             'url': self.absolute_url(a['href']),
+    #             'info': '%s | Rating: %s' % (latest, votes),
+    #         })
+    #     # end for
+
+    #     return results
+    # # end def
+
+    def read_novel_info(self):
+        '''Get novel title, autor, cover etc'''
+        logger.debug('Visiting %s', self.novel_url)
+        soup = self.get_soup(self.novel_url)
+
+        self.novel_title = soup.select_one(
+            'meta[property="og:title"]')['content']
+        logger.info('Novel title: %s', self.novel_title)
+
+        self.novel_cover = soup.select_one(
+            'meta[property="og:image"]')['content']
+        logger.info('Novel cover: %s', self.novel_cover)
+
+        self.novel_author = ' '.join([
+            a.text.strip()
+            for a in soup.select('.author-content a[href*="manga-author"]')
+        ])
+        logger.info('%s', self.novel_author)
+
+        self.novel_id = soup.select_one('#manga-chapters-holder')['data-id']
+        logger.info('Novel id: %s', self.novel_id)
+
+        response = self.submit_form(
+            chapter_list_url, data='action=manga_get_chapters&manga=' + self.novel_id)
+        soup = self.make_soup(response)
+        for a in reversed(soup.select('.wp-manga-chapter a')):
+            chap_id = len(self.chapters) + 1
+            vol_id = 1 + len(self.chapters) // 100
+            if chap_id % 100 == 1:
+                self.volumes.append({'id': vol_id})
+            # end if
+            self.chapters.append({
+                'id': chap_id,
+                'volume': vol_id,
+                'title': a.text.strip(),
+                'url':  self.absolute_url(a['href']),
+            })
+        # end for
+    # end def
+
+    @cleanup_text
+    def download_chapter_body(self, chapter):
+        '''Download body of a single chapter and return as clean html format.'''
+        logger.info('Visiting %s', chapter['url'])
+        soup = self.get_soup(chapter['url'])
+
+        contents = soup.select('.reading-content')
+
+        # Fixes images, so they can be downloaded.
+        all_imgs = soup.find_all('img')
+        for img in all_imgs:
+            if img.has_attr('data-src'):
+                src_url = img['data-src']
+                parent = img.parent
+                img.decompose()
+                new_tag = soup.new_tag("img", src=src_url)
+                parent.append(new_tag)
+
+        return str(contents)
+    # end def
+# end class
\ No newline at end of file
diff --git a/lncrawl/sources/novelmultiverse.py b/lncrawl/sources/novelmultiverse.py
new file mode 100644
index 000000000..69b99c451
--- /dev/null
+++ b/lncrawl/sources/novelmultiverse.py
@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+import json
+import logging
+import re
+from urllib.parse import urlparse
+from ..utils.crawler import Crawler
+
+logger = logging.getLogger(__name__)
+search_url = 'https://www.novelmultiverse.com/?s=%s&post_type=wp-manga&author=&artist=&release='
+chapter_list_url = 'https://www.novelmultiverse.com/wp-admin/admin-ajax.php'
+
+
+class NovelMultiverseCrawler(Crawler):
+    base_url = 'https://www.novelmultiverse.com/'
+
+    def search_novel(self, query):
+        query = query.lower().replace(' ', '+')
+        soup = self.get_soup(search_url % query)
+
+        results = []
+        for tab in soup.select('.c-tabs-item__content'):
+            a = tab.select_one('.post-title h3 a')
+            latest = tab.select_one('.latest-chap .chapter a').text
+            votes = tab.select_one('.rating .total_votes').text
+            results.append({
+                'title': a.text.strip(),
+                'url': self.absolute_url(a['href']),
+                'info': '%s | Rating: %s' % (latest, votes),
+            })
+        # end for
+
+        return results
+    # end def
+
+    def read_novel_info(self):
+        '''Get novel title, autor, cover etc'''
+        logger.debug('Visiting %s', self.novel_url)
+        soup = self.get_soup(self.novel_url)
+
+        self.novel_title = soup.select_one(
+            'meta[property="og:title"]')['content']
+        logger.info('Novel title: %s', self.novel_title)
+
+        self.novel_cover = soup.select_one(
+            'meta[property="og:image"]')['content']
+        logger.info('Novel cover: %s', self.novel_cover)
+
+        self.novel_author = ' '.join([
+            a.text.strip()
+            for a in soup.select('.author-content a[href*="novel-author"]')
+        ])
+        logger.info('%s', self.novel_author)
+
+        self.novel_id = soup.select_one('#manga-chapters-holder')['data-id']
+        logger.info('Novel id: %s', self.novel_id)
+
+        response = self.submit_form(
+            chapter_list_url, data='action=manga_get_chapters&manga=' + self.novel_id)
+        soup = self.make_soup(response)
+        for a in reversed(soup.select('.wp-manga-chapter a')):
+            chap_id = len(self.chapters) + 1
+            vol_id = 1 + len(self.chapters) // 100
+            if chap_id % 100 == 1:
+                self.volumes.append({'id': vol_id})
+            # end if
+            self.chapters.append({
+                'id': chap_id,
+                'volume': vol_id,
+                'title': a.text.strip(),
+                'url':  self.absolute_url(a['href']),
+            })
+        # end for
+    # end def
+
+    def download_chapter_body(self, chapter):
+        '''Download body of a single chapter and return as clean html format.'''
+        logger.info('Visiting %s', chapter['url'])
+        soup = self.get_soup(chapter['url'])
+        contents = soup.select('.reading-content p')
+        return ''.join([str(p) for p in contents])
+    # end def
+# end class
\ No newline at end of file
diff --git a/lncrawl/sources/novelonlinefree.py b/lncrawl/sources/novelonlinefree.py
index 941cd581e..8a41dbf1d 100644
--- a/lncrawl/sources/novelonlinefree.py
+++ b/lncrawl/sources/novelonlinefree.py
@@ -71,8 +71,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         if 'Chapter' in soup.select_one('h1').text:
             chapter['title'] = soup.select_one('h1').text
         else:
diff --git a/lncrawl/sources/novelonlinefull.py b/lncrawl/sources/novelonlinefull.py
index 1b38e3c21..63aa610c9 100644
--- a/lncrawl/sources/novelonlinefull.py
+++ b/lncrawl/sources/novelonlinefull.py
@@ -69,8 +69,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         if 'Chapter' in soup.select_one('h1').text:
             chapter['title'] = soup.select_one('h1').text
         else:
diff --git a/lncrawl/sources/novelplanet.py b/lncrawl/sources/novelplanet.py
index 063acc709..ae3c89a4b 100644
--- a/lncrawl/sources/novelplanet.py
+++ b/lncrawl/sources/novelplanet.py
@@ -66,8 +66,6 @@ def download_chapter_body(self, chapter):
 
         contents = soup.select_one('#divReadContent')
         # self.clean_contents(content)
-
-        logger.debug(soup.title.string)
         if soup.select_one('h4').text:
             chapter['title'] = soup.select_one('h4').text
         else:
diff --git a/lncrawl/sources/novelringan.py b/lncrawl/sources/novelringan.py
index 11aa4ca23..4d9c007d8 100644
--- a/lncrawl/sources/novelringan.py
+++ b/lncrawl/sources/novelringan.py
@@ -22,8 +22,13 @@ def read_novel_info(self):
             soup.select_one('div.imgprop img')['src'])
         logger.info('Novel cover: %s', self.novel_cover)
 
-        self.novel_author = 'Translated by novelringan.com'
-        logger.info('Novel author: %s', self.novel_author)
+        self.novel_author = " ".join(
+            [
+                a.text.strip()
+                for a in soup.select('.entry-author a[href*="/author/"]')
+            ]
+        )
+        logger.info("%s", self.novel_author)
 
         for a in reversed(soup.select('.bxcl ul li a')):
             chap_id = len(self.chapters) + 1
@@ -48,8 +53,7 @@ def download_chapter_body(self, chapter):
         '''Download body of a single chapter and return as clean html format.'''
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
-
-        soup.select_one('#bacotan').extract()
+        chapter['body_lock'] = True
         contents = soup.select('.entry-content p')
 
         body = [str(p) for p in contents if p.text.strip()]
diff --git a/lncrawl/sources/oppatrans.py b/lncrawl/sources/oppatrans.py
index 62fa2ae44..cf776f2f4 100644
--- a/lncrawl/sources/oppatrans.py
+++ b/lncrawl/sources/oppatrans.py
@@ -54,8 +54,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         body = []
         contents = soup.select('div.entry-content p')
         contents = contents[:-1]
diff --git a/lncrawl/sources/ornovel.py b/lncrawl/sources/ornovel.py
new file mode 100644
index 000000000..d1bfb9890
--- /dev/null
+++ b/lncrawl/sources/ornovel.py
@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+import json
+import logging
+import re
+from ..utils.crawler import Crawler
+
+logger = logging.getLogger(__name__)
+
+class OrNovel(Crawler):
+    base_url = 'https://www.ornovel.com/'
+
+    def read_novel_info(self):
+        '''Get novel title, autor, cover etc'''
+        logger.debug('Visiting %s', self.novel_url)
+        soup = self.get_soup(self.novel_url)
+
+        self.novel_title = ' '.join([
+            str(x)
+            for x in soup.select_one('.title h1').contents
+            if not x.name
+        ]).strip()
+        logger.info('Novel title: %s', self.novel_title)
+
+        probable_img = soup.select_one('.intro-left img.book-image')
+        if probable_img:
+            self.novel_cover = self.absolute_url(probable_img['src'])
+        logger.info('Novel cover: %s', self.novel_cover)
+
+        self.novel_author = ' '.join([
+            a.text.strip()
+            for a in soup.select('.author-container')
+        ])
+        logger.info('%s', self.novel_author)
+
+        volumes = set()
+        chapters = soup.select('ul.chapters-all li.chapters-item a')
+        for a in chapters:
+            chap_id = len(self.chapters) + 1
+            vol_id = (chap_id - 1) // 100 + 1
+            volumes.add(vol_id)
+            self.chapters.append({
+                'id': chap_id,
+                'volume': vol_id,
+                'url':  self.absolute_url(a['href']),
+                'title': a.text.strip() or ('Chapter %d' % chap_id),
+            })
+        # end for
+
+        self.volumes = [{'id': x} for x in volumes]
+    # end def
+
+    def download_chapter_body(self, chapter):
+        '''Download body of a single chapter and return as clean html format.'''
+        logger.info('Downloading %s', chapter['url'])
+        soup = self.get_soup(chapter['url'])
+
+        contents = soup.select_one('div.chapter-detail')
+        for bad in contents.select('h2, ins, .chapter-header .code-block, script, .adsbygoogle'):
+            bad.decompose()
+
+        return str(contents)
+    # end def
+# end class
\ No newline at end of file
diff --git a/lncrawl/sources/readnovelz.py b/lncrawl/sources/readnovelz.py
new file mode 100644
index 000000000..7dca5cc37
--- /dev/null
+++ b/lncrawl/sources/readnovelz.py
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+import json
+import logging
+import re
+from ..utils.crawler import Crawler
+
+logger = logging.getLogger(__name__)
+search_url = 'https://readnovelz.net/?s=%s&post_type=wp-manga&author=&artist=&release='
+
+
+class ReadNovelzCrawler(Crawler):
+    base_url = 'https://readnovelz.net/'
+
+    def search_novel(self, query):
+        query = query.lower().replace(' ', '+')
+        soup = self.get_soup(search_url % query)
+
+        results = []
+        for tab in soup.select('.c-tabs-item__content'):
+            a = tab.select_one('.post-title h3 a')
+            latest = tab.select_one('.latest-chap .chapter a').text
+            votes = tab.select_one('.rating .total_votes').text
+            results.append({
+                'title': a.text.strip(),
+                'url': self.absolute_url(a['href']),
+                'info': '%s | Rating: %s' % (latest, votes),
+            })
+        # end for
+
+        return results
+    # end def
+
+    def read_novel_info(self):
+        '''Get novel title, autor, cover etc'''
+        logger.debug('Visiting %s', self.novel_url)
+        soup = self.get_soup(self.novel_url)
+
+        self.novel_title = soup.select_one(
+            'meta[property="og:title"]')['content']
+        logger.info('Novel title: %s', self.novel_title)
+
+        self.novel_cover = soup.select_one(
+            'meta[property="og:image"]')['content']
+        logger.info('Novel cover: %s', self.novel_cover)
+
+        author = soup.select('.author-content a')
+        if len(author) == 2:
+            self.novel_author = author[0].text + ' (' + author[1].text + ')'
+        else:
+            self.novel_author = author[0].text
+        logger.info('Novel author: %s', self.novel_author)
+
+        volumes = set()
+        chapters = soup.select('tbody.main td.wp-manga-chapter a')
+        for a in reversed(chapters):
+            chap_id = len(self.chapters) + 1
+            vol_id = (chap_id - 1) // 100 + 1
+            volumes.add(vol_id)
+            self.chapters.append({
+                'id': chap_id,
+                'volume': vol_id,
+                'url':  self.absolute_url(a['href']),
+                'title': a.text.strip() or ('Chapter %d' % chap_id),
+            })
+        # end for
+
+        self.volumes = [{'id': x} for x in volumes]
+    # end def
+
+    def download_chapter_body(self, chapter):
+        '''Download body of a single chapter and return as clean html format.'''
+        logger.info('Downloading %s', chapter['url'])
+        soup = self.get_soup(chapter['url'])
+
+        contents = soup.select_one('div.text-left')
+        for bad in contents.select('h3, .code-block, script, .adsbygoogle'):
+            bad.decompose()
+
+        return str(contents)
+    # end def
+# end class
\ No newline at end of file
diff --git a/lncrawl/sources/reincarnationpalace.py b/lncrawl/sources/reincarnationpalace.py
index 5ab57f33c..2c6c89dda 100644
--- a/lncrawl/sources/reincarnationpalace.py
+++ b/lncrawl/sources/reincarnationpalace.py
@@ -16,11 +16,12 @@ def read_novel_info(self):
         logger.debug('Visiting %s', self.novel_url)
         soup = self.get_soup(self.novel_url)
 
-        self.novel_title = soup.select_one('h1.entry-title').text.strip()
+        self.novel_title = soup.select_one(
+            'meta[property="og:title"]')['content']
         logger.info('Novel title: %s', self.novel_title)
 
         self.novel_cover = self.absolute_url(
-            soup.select_one('div.elementor-image img')['data-src'])
+            soup.select_one('div.elementor-image img')['src'])
         logger.info('Novel cover: %s', self.novel_cover)
 
         self.novel_author = soup.select('div.elementor-widget-container p')[6].text.strip()
diff --git a/lncrawl/sources/royalroad.py b/lncrawl/sources/royalroad.py
index abbf9199d..2ae5d436a 100644
--- a/lncrawl/sources/royalroad.py
+++ b/lncrawl/sources/royalroad.py
@@ -72,8 +72,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         if 'Chapter' in soup.select_one('h2').text:
             chapter['title'] = soup.select_one('h2').text
         else:
diff --git a/lncrawl/sources/rpgnovels.py b/lncrawl/sources/rpgnovels.py
new file mode 100644
index 000000000..7b962c3cf
--- /dev/null
+++ b/lncrawl/sources/rpgnovels.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+import json
+import logging
+import re
+from ..utils.cleaner import cleanup_text
+from ..utils.crawler import Crawler
+
+logger = logging.getLogger(__name__)
+
+class RPGNovels(Crawler):
+    base_url = [
+        'https://rpgnovels.com/',
+        'https://rpgnoob.wordpress.com/'
+        ]
+
+    def read_novel_info(self):
+        '''Get novel title, autor, cover etc'''
+        logger.debug('Visiting %s', self.novel_url)
+        soup = self.get_soup(self.novel_url)
+
+        self.novel_title = soup.select_one(
+            'meta[property="og:title"]')['content']
+        logger.info('Novel title: %s', self.novel_title)
+
+        self.novel_cover = soup.select_one(
+            'meta[property="og:image"]')['content']
+        logger.info('Novel cover: %s', self.novel_cover)
+
+        self.novel_author = " ".join(
+            [
+                a.text.strip()
+                for a in soup.select('.post-entry a[href*="mypage.syosetu.com"]')
+            ]
+        )
+        logger.info("%s", self.novel_author)
+
+        # Removes none TOC links from bottom of page.
+        toc_parts = soup.select_one('div.post-entry')
+        for notoc in toc_parts.select('.sharedaddy'):
+            notoc.decompose()
+
+        # Extract volume-wise chapter entries
+        # Stops external links being selected as chapters
+        chapters = soup.select(
+            'div.post-entry li a[href*="rpg"]')
+
+        for a in chapters:
+            chap_id = len(self.chapters) + 1
+            if len(self.chapters) % 100 == 0:
+                vol_id = chap_id//100 + 1
+                vol_title = 'Volume ' + str(vol_id)
+                self.volumes.append({
+                    'id': vol_id,
+                    'title': vol_title,
+                })
+            # end if
+            self.chapters.append({
+                'id': chap_id,
+                'volume': vol_id,
+                'url':  self.absolute_url(a['href']),
+                'title': a.text.strip() or ('Chapter %d' % chap_id),
+            })
+        # end for
+    # end def
+
+    @cleanup_text
+    def download_chapter_body(self, chapter):
+        '''Download body of a single chapter and return as clean html format.'''
+        logger.info('Downloading %s', chapter['url'])
+        soup = self.get_soup(chapter['url'])
+
+        body_parts = soup.select_one('div.post-entry')
+
+        # Removes "Share this" text and buttons from bottom of chapters. Also other junk on page.
+        for share in body_parts.select('.sharedaddy, .inline-ad-slot, .code-block, script, hr, .adsbygoogle, a[href*="https://www.patreon.com/rpgnovels"]'):
+            share.decompose()
+
+        # Remoeves bad text from chapters.
+        for content in body_parts.select("p"):
+            for bad in ["Previous Chapter", "Table of Contents", "Next Chapter", "Please consider supporting me. You can do this either by turning off adblock for this blog or via my . Thank you."]:
+                if bad in content.text:
+                    content.decompose()
+
+        # Fixes images, so they can be downloaded.
+        all_imgs = soup.find_all('img')
+        for img in all_imgs:
+            if img.has_attr('data-orig-file'):
+                src_url = img['src']
+                parent = img.parent
+                img.decompose()
+                new_tag = soup.new_tag("img", src=src_url)
+                parent.append(new_tag)
+
+        return str(body_parts)
+    # end def
+# end class
\ No newline at end of file
diff --git a/lncrawl/sources/scribblehub.py b/lncrawl/sources/scribblehub.py
index 152b70f6c..9ed8996f0 100644
--- a/lncrawl/sources/scribblehub.py
+++ b/lncrawl/sources/scribblehub.py
@@ -13,24 +13,23 @@
 class ScribbleHubCrawler(Crawler):
     base_url = 'https://www.scribblehub.com/'
 
-    # TODO: disabled due to cloudflare issue
-    # def search_novel(self, query):
-    #     url = search_url % quote(query.lower())
-    #     logger.debug('Visiting %s', url)
-    #     soup = self.get_soup(url)
-
-    #     results = []
-    #     for novel in soup.select('div.search_body'):
-    #         a = novel.select_one('.search_title a')
-    #         info = novel.select_one('.search_stats').text.strip()
-    #         results.append({
-    #             'title': a.text.strip(),
-    #             'url': self.absolute_url(a['href']),
-    #             'info': info,
-    #         })
-    #     # end for
-    #     return results
-    # # end def
+    def search_novel(self, query):
+        url = search_url % quote(query.lower())
+        logger.debug('Visiting %s', url)
+        soup = self.get_soup(url)
+
+        results = []
+        for novel in soup.select('div.search_body'):
+            a = novel.select_one('.search_title a')
+            info = novel.select_one('.search_stats').text.strip()
+            results.append({
+                'title': a.text.strip(),
+                'url': self.absolute_url(a['href']),
+                'info': info,
+            })
+        # end for
+        return results
+    # end def
 
     def read_novel_info(self):
         '''Get novel title, autor, cover etc'''
@@ -89,8 +88,6 @@ def download_chapter_body(self, chapter):
         '''Download body of a single chapter and return as clean html format.'''
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
-
-        logger.debug(soup.title.string)
         contents = soup.find('div', {'id': 'chp_raw'})
 
         #body_parts = []
diff --git a/lncrawl/sources/shalvation.py b/lncrawl/sources/shalvation.py
index fdd24cdd6..771723172 100644
--- a/lncrawl/sources/shalvation.py
+++ b/lncrawl/sources/shalvation.py
@@ -56,8 +56,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         body_parts = soup.select_one('div.entry-content')
 
         # Removes "Share this" text and buttons from bottom of chapters.
diff --git a/lncrawl/sources/shinsori.py b/lncrawl/sources/shinsori.py
index 0d9f06f65..730f45dd9 100644
--- a/lncrawl/sources/shinsori.py
+++ b/lncrawl/sources/shinsori.py
@@ -60,8 +60,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         content = soup.select_one('div.entry-content')
 
         # remove div with no class
diff --git a/lncrawl/sources/smnovels.py b/lncrawl/sources/smnovels.py
new file mode 100644
index 000000000..4b00b916a
--- /dev/null
+++ b/lncrawl/sources/smnovels.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+import logging
+from ..utils.crawler import Crawler
+
+logger = logging.getLogger(__name__)
+
+class SMNovelsCrawler(Crawler):
+    base_url = 'https://smnovels.com/'
+
+    def read_novel_info(self):
+        '''Get novel title, autor, cover etc'''
+        logger.debug('Visiting %s', self.novel_url)
+        soup = self.get_soup(self.novel_url)
+
+        # Site has no author name or novel covers.
+        self.novel_title = soup.select_one('h1.entry-title').text.strip()
+        logger.info('Novel title: %s', self.novel_title)
+
+        for a in soup.select('.all-chapters-list a'):
+            chap_id = len(self.chapters) + 1
+            vol_id = len(self.chapters) // 100 + 1
+            if len(self.chapters) % 100 == 0:
+                self.volumes.append({'id': vol_id})
+            # end if
+            self.chapters.append({
+                'id': chap_id,
+                'volume': vol_id,
+                'title': a.text.strip(),
+                'url': self.absolute_url(a['href']),
+            })
+        # end for
+    # end def
+
+    def download_chapter_body(self, chapter):
+        '''Download body of a single chapter and return as clean html format.'''
+        logger.info('Downloading %s', chapter['url'])
+        soup = self.get_soup(chapter['url'])
+
+        contents = soup.select_one('.entry-content')
+        for bad in contents.select('br'):
+            bad.decompose()
+        body = self.extract_contents(contents)
+        return '<p>' + '</p><p>'.join(body) + '</p>'
+    # end def
+# end class
\ No newline at end of file
diff --git a/lncrawl/sources/virlyce.py b/lncrawl/sources/virlyce.py
index e3d30d27b..ebc4eb5db 100644
--- a/lncrawl/sources/virlyce.py
+++ b/lncrawl/sources/virlyce.py
@@ -54,8 +54,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         body_parts = soup.select_one('div.entry-content')
 
         # Removes "Share this" text and buttons from bottom of chapters.
diff --git a/lncrawl/sources/vistrans.py b/lncrawl/sources/vistrans.py
index 05161519e..993e868f6 100644
--- a/lncrawl/sources/vistrans.py
+++ b/lncrawl/sources/vistrans.py
@@ -18,18 +18,22 @@ def read_novel_info(self):
         self.novel_title = soup.find("h1", {"class": "entry-title"}).text.strip()
         logger.info('Novel title: %s', self.novel_title)
 
-        # NOTE: Could not get cover kept coming back with error.
-        # self.novel_cover = self.absolute_url(
-        #     soup.select_one('div.entry-content div.wp-block-media-text img')['data-orig-file'])
+        # TODO: Could not get cover kept coming back with error "(cannot identify image file <_io.BytesIO object at 0x00000179D0D8FEA0>)"
+        # self.novel_cover = soup.select_one(
+        #     'meta[property="og:image"]')['content']
         # logger.info('Novel cover: %s', self.novel_cover)
 
-        # Could not get author name from site, just replaced with translators name.
-        self.novel_author = "by VisTranslations"
-        logger.info('Novel author: %s', self.novel_author)
+        self.novel_author = soup.select_one('div.wp-block-media-text__content > p:nth-child(4)').text
+        logger.info('%s', self.novel_author)
+
+        # Removes none TOC links from bottom of page.
+        toc_parts = soup.select_one('.site-content')
+        for notoc in toc_parts.select('.sharedaddy, iframe'):
+            notoc.decompose()
 
         # Extract volume-wise chapter entries
-        # FIXME: Sometimes grabs social media link at bottom of page, No idea how to exclude links.
-        chapters = soup.select('table td [href*="vistranslations.wordpress.com"]')
+        # TODO: Some chapters have two href links for each chapters so you get duplicate chapters.
+        chapters = soup.select('table td a[href*="vistranslations.wordpress.com"]')
 
         for a in chapters:
             chap_id = len(self.chapters) + 1
@@ -55,8 +59,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         body = []
         contents = soup.select('div.entry-content p')
         for p in contents:
diff --git a/lncrawl/sources/wattpad.py b/lncrawl/sources/wattpad.py
index 1b4856a91..61b7b9a2e 100755
--- a/lncrawl/sources/wattpad.py
+++ b/lncrawl/sources/wattpad.py
@@ -8,6 +8,7 @@
 logger = logging.getLogger(__name__)
 
 chapter_info_url = 'https://www.wattpad.com/v4/parts/%s?fields=id,title,pages,text_url&_=%d'
+story_info_url = 'https://www.wattpad.com/api/v3/stories/%s'
 
 class WattpadCrawler(Crawler):
     base_url = [
@@ -21,21 +22,24 @@ def initialize(self):
     def read_novel_info(self):
         '''Get novel title, autor, cover etc'''
 
-        logger.debug('Visiting %s', self.novel_url)
-        soup = self.get_soup(self.novel_url)
+        search_id = re.compile(r'\d{9,}')
+        id_no = search_id.search(self.novel_url)
+        story_url = story_info_url % (id_no.group())
 
-        self.novel_title = soup.select_one('.story-info__title').get_text().strip()
+        logger.debug('Visiting %s', story_url)
+        story_info = self.get_json(story_url)
+
+        self.novel_title = story_info['title']
         logger.info('Novel title: %s', self.novel_title)
 
         self.novel_cover = self.absolute_url(
-            soup.select_one('.story-cover img')['src'])
+            story_info['cover'])
         logger.info('Novel cover: %s', self.novel_cover)
 
-        self.novel_author = soup.select_one(
-            '.author-info__username').get_text()
+        self.novel_author = story_info['user']['name']
         logger.info('Novel author: %s', self.novel_author)
 
-        chapters = soup.select_one('.story-parts').select('ul li a')
+        chapters = story_info['parts']
 
         vols = set([])
         for a in chapters:
@@ -45,8 +49,8 @@ def read_novel_info(self):
             self.chapters.append({
                 'id': chap_id,
                 'volume': vol_id,
-                'url':  self.absolute_url(a['href']),
-                'title': a.text.strip() or ('Chapter %d' % chap_id),
+                'url':  self.absolute_url(a['url']),
+                'title': a['title'] or ('Chapter %d' % chap_id),
             })
         # end for
         self.volumes = [{'id': i} for i in vols]
@@ -62,7 +66,7 @@ def download_chapter_body(self, chapter):
         
         text_url = data['text_url']['text']
         logger.info('Getting text %s', text_url)
-        text = self.get_response(text_url).content.decode('utf-8')
+        text = self.get_response(text_url).content.decode('utf8')
         text = re.sub(r'<p data-p-id="[a-f0-9]+>"', '<p>', text)
         return text
     # end def
diff --git a/lncrawl/sources/wnmtl.py b/lncrawl/sources/wnmtl.py
new file mode 100644
index 000000000..948544187
--- /dev/null
+++ b/lncrawl/sources/wnmtl.py
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+import logging
+from ..utils.crawler import Crawler
+
+logger = logging.getLogger(__name__)
+search_url = 'https://www.wnmtl.org/?s=%s&post_type=wp-manga&author=&artist=&release='
+chapter_list_url = 'https://www.wnmtl.org/wp-admin/admin-ajax.php'
+
+
+class WNMTLCrawler(Crawler):
+    base_url = 'https://www.wnmtl.org/'
+
+    # TODO: Disable for now as site is loading very slowly, sometimes it's find others it timesout.
+    # def search_novel(self, query):
+    #     query = query.lower().replace(' ', '+')
+    #     soup = self.get_soup(search_url % query)
+
+    #     results = []
+    #     for tab in soup.select('.c-tabs-item__content'):
+    #         a = tab.select_one('.post-title h3 a')
+    #         latest = tab.select_one('.latest-chap .chapter a').text
+    #         votes = tab.select_one('.rating .total_votes').text
+    #         results.append({
+    #             'title': a.text.strip(),
+    #             'url': self.absolute_url(a['href']),
+    #             'info': '%s | Rating: %s' % (latest, votes),
+    #         })
+    #     # end for
+
+    #     return results
+    # # end def
+
+    def read_novel_info(self):
+        '''Get novel title, autor, cover etc'''
+        logger.debug('Visiting %s', self.novel_url)
+        soup = self.get_soup(self.novel_url)
+
+        possible_title = soup.select_one('.post-title h1')
+        for span in possible_title.select('span'):
+            span.extract()
+        # end for
+        self.novel_title = possible_title.text.strip()
+        logger.info('Novel title: %s', self.novel_title)
+
+        self.novel_cover = self.absolute_url(
+            soup.select_one('.summary_image a img')['src'])
+        logger.info('Novel cover: %s', self.novel_cover)
+
+        self.novel_author = ' '.join([
+            a.text.strip()
+            for a in soup.select('.author-content a[href*="manga-author"]')
+        ])
+        logger.info('%s', self.novel_author)
+
+        self.novel_id = soup.select_one('#manga-chapters-holder')['data-id']
+        logger.info('Novel id: %s', self.novel_id)
+
+        response = self.submit_form(
+            chapter_list_url, data='action=manga_get_chapters&manga=' + self.novel_id)
+        soup = self.make_soup(response)
+        for a in reversed(soup.select('.wp-manga-chapter a')):
+            chap_id = len(self.chapters) + 1
+            vol_id = 1 + len(self.chapters) // 100
+            if chap_id % 100 == 1:
+                self.volumes.append({'id': vol_id})
+            # end if
+            self.chapters.append({
+                'id': chap_id,
+                'volume': vol_id,
+                'title': a.text.strip(),
+                'url':  self.absolute_url(a['href']),
+            })
+        # end for
+    # end def
+
+    def download_chapter_body(self, chapter):
+        '''Download body of a single chapter and return as clean html format.'''
+        logger.info('Visiting %s', chapter['url'])
+        soup = self.get_soup(chapter['url'])
+        contents = soup.select('.reading-content p')
+        return ''.join([str(p) for p in contents])
+    # end def
+# end class
\ No newline at end of file
diff --git a/lncrawl/sources/worldnovelonline.py b/lncrawl/sources/worldnovelonline.py
index afd02092b..9f6ba4987 100644
--- a/lncrawl/sources/worldnovelonline.py
+++ b/lncrawl/sources/worldnovelonline.py
@@ -1,11 +1,5 @@
 # -*- coding: utf-8 -*-
 import logging
-import re
-import json
-from urllib.parse import quote, urlparse
-import urllib.parse
-from bs4 import BeautifulSoup
-
 from ..utils.crawler import Crawler
 
 logger = logging.getLogger(__name__)
@@ -139,8 +133,8 @@ def download_chapter_body(self, chapter):
 
         contents = soup.select_one(', '.join([
             '.post-content'
-            '.cha-words',
-            '.cha-content',
+            # '.cha-words',
+            # '.cha-content',
             # '.chapter-fill',
             '.entry-content.cl',
             '#content',
@@ -155,6 +149,6 @@ def download_chapter_body(self, chapter):
             codeblock.decompose()
         # end for
 
-        return str(contents)
+        return str(contents).replace('www.worldnovel.online', '')
     # end def
 # end class
diff --git a/lncrawl/sources/wujizun.py b/lncrawl/sources/wujizun.py
new file mode 100644
index 000000000..99722c7bc
--- /dev/null
+++ b/lncrawl/sources/wujizun.py
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+import json
+import logging
+import re
+from ..utils.cleaner import cleanup_text
+from ..utils.crawler import Crawler
+
+logger = logging.getLogger(__name__)
+
+class Wujizun(Crawler):
+    base_url = 'https://wujizun.com/'
+
+    def read_novel_info(self):
+        '''Get novel title, autor, cover etc'''
+        logger.debug('Visiting %s', self.novel_url)
+        soup = self.get_soup(self.novel_url)
+
+        self.novel_title = soup.select_one(
+            'meta[property="og:title"]')['content']
+        logger.info('Novel title: %s', self.novel_title)
+
+        self.novel_cover = soup.select_one(
+            'meta[property="og:image"]')['content']
+        logger.info('Novel cover: %s', self.novel_cover)
+
+        self.novel_author = "Translated by Wujizun"
+        logger.info('Novel author: %s', self.novel_author)
+
+        # Removes none TOC links from bottom of page.
+        toc_parts = soup.select_one('div.entry-content')
+        for notoc in toc_parts.select('.sharedaddy, .ezoic-adpicker-ad, .ezoic-ad-adaptive, .ezoic-ad'):
+            notoc.decompose()
+
+        # Extract volume-wise chapter entries
+        # Stops external links being selected as chapters
+        chapters = soup.select(
+            'div.entry-content p a[href*="wujizun.com/2"]')
+
+        for a in chapters:
+            chap_id = len(self.chapters) + 1
+            if len(self.chapters) % 100 == 0:
+                vol_id = chap_id//100 + 1
+                vol_title = 'Volume ' + str(vol_id)
+                self.volumes.append({
+                    'id': vol_id,
+                    'title': vol_title,
+                })
+            # end if
+            self.chapters.append({
+                'id': chap_id,
+                'volume': vol_id,
+                'url':  self.absolute_url(a['href']),
+                'title': a.text.strip() or ('Chapter %d' % chap_id),
+            })
+        # end for
+    # end def
+
+    @cleanup_text
+    def download_chapter_body(self, chapter):
+        '''Download body of a single chapter and return as clean html format.'''
+        logger.info('Downloading %s', chapter['url'])
+        soup = self.get_soup(chapter['url'])
+
+        body_parts = soup.select_one('div.entry-content')
+
+        # Removes "Share this" text and buttons from bottom of chapters. Also other junk on page.
+        for share in body_parts.select('iframe, .sharedaddy, .jp-relatedposts, .inline-ad-slot, .code-block, script, hr, .adsbygoogle, .ezoic-adpicker-ad, .ezoic-ad-adaptive, .ezoic-ad, .cb_p6_patreon_button, a[href*="patreon.com"]'):
+            share.decompose()
+
+        # Remoeves bad text from chapters.
+        for content in body_parts.select("p"):
+            for bad in ["Previous Chapter", "Table of Contents", "Next Chapter", "MYSD Patreon:"]:
+                if bad in content.text:
+                    content.decompose()
+
+        # Fixes images, so they can be downloaded.
+        all_imgs = soup.find_all('img')
+        for img in all_imgs:
+            if img.has_attr('data-orig-file'):
+                src_url = img['src']
+                parent = img.parent
+                img.decompose()
+                new_tag = soup.new_tag("img", src=src_url)
+                parent.append(new_tag)
+
+        return str(body_parts)
+    # end def
+# end class
\ No newline at end of file
diff --git a/lncrawl/sources/wuxiacom.py b/lncrawl/sources/wuxiacom.py
index 87ae02d71..abe0f4298 100644
--- a/lncrawl/sources/wuxiacom.py
+++ b/lncrawl/sources/wuxiacom.py
@@ -22,7 +22,7 @@ def search_novel(self, query):
         url = search_url % query
         logger.info('Visiting %s ...', url)
         data = self.get_json(url)
-        logger.debug(data)
+        # logger.debug(data)
 
         results = []
         for item in data['items'][:5]:
diff --git a/lncrawl/sources/wuxiaonline.py b/lncrawl/sources/wuxiaonline.py
index c53665358..dbd17f7e6 100644
--- a/lncrawl/sources/wuxiaonline.py
+++ b/lncrawl/sources/wuxiaonline.py
@@ -39,9 +39,8 @@ def read_novel_info(self):
         self.novel_title = soup.select_one('h1.entry-title').text
         logger.info('Novel title: %s', self.novel_title)
 
-        # self.novel_author = soup.select_one('#maininfo p').text.strip()
-        # self.novel_author = re.sub(r'^Author[^\w]+', '', self.novel_author).strip()
-        # logger.info('Novel author: %s', self.novel_author)
+        self.novel_author = soup.select_one('div.entry-header > div.truyen_if_wrap > ul > li:nth-child(2)').text
+        logger.info('%s', self.novel_author)
 
         self.novel_cover = self.absolute_url(
             soup.select_one('.info_image img')['src'])
diff --git a/lncrawl/sources/wuxiaworldio.py b/lncrawl/sources/wuxiaworldio.py
index 982378891..078cc58b1 100644
--- a/lncrawl/sources/wuxiaworldio.py
+++ b/lncrawl/sources/wuxiaworldio.py
@@ -68,8 +68,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         if 'Chapter' in soup.select_one('h1').text:
             chapter['title'] = soup.select_one('h1').text
         else:
diff --git a/lncrawl/sources/wuxiaworldlive.py b/lncrawl/sources/wuxiaworldlive.py
index 1e3325120..71ada8d1b 100644
--- a/lncrawl/sources/wuxiaworldlive.py
+++ b/lncrawl/sources/wuxiaworldlive.py
@@ -75,8 +75,6 @@ def download_chapter_body(self, chapter):
         logger.info('Downloading %s', chapter['url'])
         soup = self.get_soup(chapter['url'])
 
-        logger.debug(soup.title.string)
-
         if 'Chapter' in soup.select_one('h1').text:
             chapter['title'] = soup.select_one('h1').text
         else:
diff --git a/lncrawl/utils/crawler.py b/lncrawl/utils/crawler.py
index c3bca34aa..a0ecb8fbe 100644
--- a/lncrawl/utils/crawler.py
+++ b/lncrawl/utils/crawler.py
@@ -172,11 +172,13 @@ def get_response(self, url, **kargs):
         #kargs.setdefault('allow_redirects', True)
         kargs.setdefault('timeout', 150)  # in seconds
         headers = kargs.setdefault('headers', {})
-        headers.setdefault('User-Agent', _default_user_agent)
+        
+        headers = {k.lower(): v for k, v in headers.items()}
+        headers.setdefault('user-agent', _default_user_agent)
 
         response = self.scraper.get(url, **kargs)
         response.raise_for_status()
-        response.encoding = 'utf-8'
+        response.encoding = 'utf8'
         self.cookies.update({
             x.name: x.value
             for x in response.cookies
@@ -189,18 +191,19 @@ def post_response(self, url, data={}, headers={}):
         if self._destroyed:
             return None
 
-        headers.setdefault('User-Agent', _default_user_agent)
-        headers.setdefault('Content-Type', 'application/json')
+        headers = {k.lower(): v for k, v in headers.items()}
+        headers.setdefault('user-agent', _default_user_agent)
+        headers.setdefault('content-type', 'application/json')
         logger.debug('POST url=%s, data=%s, headers=%s', url, data, headers)
 
         response = self.scraper.post(
             url,
             data=data,
             headers=headers,
-            #verify=False,
-            #allow_redirects=True,
+            # verify=False,
+            # allow_redirects=True,
         )
-        response.encoding = 'utf-8'
+        response.encoding = 'utf8'
         self.cookies.update({
             x.name: x.value
             for x in response.cookies
@@ -219,7 +222,8 @@ def submit_form(self, url, data={}, multipart=False, headers={}):
         if multipart:
             content_type = 'multipart/form-data'
         # end if
-        headers.setdefault('Content-Type', content_type)
+        headers = {k.lower(): v for k, v in headers.items()}
+        headers.setdefault('content-type', content_type)
         return self.post_response(url, data, headers)
     # end def
 
@@ -249,7 +253,8 @@ def make_soup(self, response, parser=None):
     def get_json(self, *args, **kwargs):
         kwargs = kwargs or dict()
         headers = kwargs.setdefault('headers', {})
-        headers.setdefault('Accept', 'application/json, text/javascript, */*')
+        headers = {k.lower(): v for k, v in headers.items()}
+        headers.setdefault('accept', 'application/json, text/javascript, */*')
         response = self.get_response(*args, **kwargs)
         return response.json()
     # end def
@@ -260,7 +265,8 @@ def post_soup(self, url, data={}, headers={}, parser='lxml'):
     # end def
 
     def post_json(self, url, data={}, headers={}):
-        headers.setdefault('Accept', 'application/json, text/javascript, */*')
+        headers = {k.lower(): v for k, v in headers.items()}
+        headers.setdefault('accept', 'application/json, text/javascript, */*')
         response = self.post_response(url, data, headers)
         return response.json()
     # end def
@@ -278,7 +284,7 @@ def download_cover(self, output_file):
         r'^[\W\D]*(volume|chapter)[\W\D]+\d+[\W\D]*$',
     ]
     bad_tags = [
-        'noscript', 'script', 'iframe', 'form', 'hr', 'img', 'ins',
+        'noscript', 'script', 'iframe', 'form', 'hr', 'ins',
         'button', 'input', 'amp-auto-ads', 'pirate'
     ]
     block_tags = [
@@ -316,8 +322,8 @@ def clean_contents(self, div):
                 tag.extract()   # Remove empty tags
             elif self.is_blacklisted(tag.text):
                 tag.extract()   # Remove blacklisted contents
-            elif hasattr(tag, 'attrs'):
-                tag.attrs = {}    # Remove attributes
+            elif hasattr(tag, 'attrs') and tag != 'img':
+                tag.attrs = {}  # Remove attributes
             # end if
         # end for
         return div
diff --git a/lncrawl/utils/racovimge.py b/lncrawl/utils/racovimge.py
index d6e4272c5..c991c3064 100644
--- a/lncrawl/utils/racovimge.py
+++ b/lncrawl/utils/racovimge.py
@@ -81,7 +81,7 @@ def random_cover(title, author):
     font = Path(font)
     with font.open('rb') as file:
         font_data = file.read()
-        font_data = base64.b64encode(font_data).decode('utf-8')
+        font_data = base64.b64encode(font_data).decode('utf8')
     # end with
     font_name = font.stem
     font_type = font_mimetypes[font.suffix.lstrip('.')]
diff --git a/lncrawl/utils/update_checker.py b/lncrawl/utils/update_checker.py
index eb513ca7a..4bccb5320 100644
--- a/lncrawl/utils/update_checker.py
+++ b/lncrawl/utils/update_checker.py
@@ -53,7 +53,7 @@ def check_update_in_background():
 def check_updates():
     # Check the last update file
     try:
-        with open(update_file, encoding='utf8') as fp:
+        with open(update_file, 'r', encoding='utf8') as fp:
             data = json.load(fp)
             latest_version = version.parse(data['info']['version'])
             if latest_version > current_version:
diff --git a/requirements-dev.txt b/requirements-dev.txt
index b4d78c9b5..2c5265464 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,5 +1,5 @@
-Jinja2
-PyInstaller
+jinja2
+pyinstaller
 cairosvg
 win_unicode_console
 setuptools
diff --git a/requirements.txt b/requirements.txt
index 895b67f81..3fb2de9f1 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-# App requirements
+# app requirements
 cryptography<=3.3.2
 ascii
 regex
@@ -10,8 +10,8 @@ python-slugify>=4.0.0,<5.0.0
 cssutils>=1.0.0,<1.1.0
 colorama>=0.4.0<0.5.0
 progress>=1.5,<2.0
-Js2Py==0.70
-EbookLib>=0.17.0,<1.0.0
+js2py==0.70
+ebooklib>=0.17.0,<1.0.0
 pillow>=6.0.0
 cloudscraper>=1.2.58
 lxml>=4.0.0,<5.0.0
@@ -21,7 +21,7 @@ html5lib~=1.0
 pycryptodome>=3.0.0,<4.0.0
 base58~=2.1.0
 
-# Bot requirements
+# bot requirements
 discord.py>=1.0.0,<2.0.0
 python-telegram-bot<12
 pydrive>=1.3.1,<2.0.0
diff --git a/setup.py b/setup.py
index 169b013df..0f6af3fba 100644
--- a/setup.py
+++ b/setup.py
@@ -18,12 +18,12 @@
     from setuptools import config, setup
 
     def parse_version(filename):
-        with open(filename, 'r') as f:
+        with open(filename, 'r', encoding='utf8') as f:
             return f.read().strip()
     # end def
 
     def parse_requirements(filename):
-        with open(filename, 'r', encoding='utf-8') as f:
+        with open(filename, 'r', encoding='utf8') as f:
             requirements = f.read().strip().split('\n')
             requirements = [
                 r.strip() for r in requirements


Test patch:
diff --git a/lncrawl/bots/test/test_inputs.py b/lncrawl/bots/test/test_inputs.py
index 6089ad252..d43b273d2 100644
--- a/lncrawl/bots/test/test_inputs.py
+++ b/lncrawl/bots/test/test_inputs.py
@@ -317,5 +317,50 @@
     ],
     'https://readlightnovels.net/': [
         'https://readlightnovels.net/death-march-kara-hajimaru-isekai-kyusoukyoku.html'
+    ],
+    'https://isotls.com/': [
+        'https://isotls.com/poisonous-peasant-concubine/'
+    ],
+    'https://lazybirdtranslations.wordpress.com/': [
+        'https://lazybirdtranslations.wordpress.com/the-prestigious-familys-young-lady-and-the-farmer/'
+    ],
+    'https://www.novelmultiverse.com/': [
+        'https://www.novelmultiverse.com/novel/the-fierce-illegitimate-miss/'
+    ],
+    'https://readnovelz.net/': [
+        'https://readnovelz.net/read/rebirth-how-a-loser-became-a-prince-charming/'
+    ],
+    'https://rpgnovels.com/': [
+        'https://rpgnovels.com/the-demon-lords-urban-development-the-strongest-dungeon-is-a-modern-day-town/'
+    ],
+    'https://dobelyuwai.wordpress.com/': [
+        'https://dobelyuwai.wordpress.com/to-be-a-power-in-the-shadows-ln-volume-4/'
+    ],
+    'https://cclawtranslations.home.blog/': [
+        'https://cclawtranslations.home.blog/asahina-wakaba-to-marumaru-na-kareshi-toc/'
+    ],
+    'https://wujizun.com/': [
+        'https://wujizun.com/mysd/'
+    ],
+    'https://novelmic.com/': [
+        'https://novelmic.com/manga/tales-of-demons-and-gods-comics/'
+    ],
+    'https://www.1ksy.com/': [
+        'https://www.1ksy.com/120_120546/'
+    ],
+    'http://readonlinenovels.com/': [
+        'http://readonlinenovels.com/nv/244c8160d7f91754/hail-the-king-h'
+    ],
+    'https://arnovel.me/': [
+        'https://arnovel.me/novel/%d8%b2%d9%88%d8%ac%d8%aa%d9%87-%d8%a7%d9%84%d8%b9%d8%a8%d9%82%d8%b1%d9%8a%d8%a9-%d9%86%d8%ac%d9%85%d8%a9/'
+    ],
+    'https://lightnovelshub.com/': [
+        'https://lightnovelshub.com/novel/strongest-abandoned-son/'
+    ],
+    'https://smnovels.com/': [
+        'https://smnovels.com/category/novel/a-dish-best-served-cold/'
+    ],
+    'https://www.wnmtl.org/': [
+        'https://www.wnmtl.org/novel/the-amber-sword/'
     ]
 }