diff --git a/src/all/nhentai/build.gradle b/src/all/nhentai/build.gradle
index a5cc9fc66..3163fa863 100644
--- a/src/all/nhentai/build.gradle
+++ b/src/all/nhentai/build.gradle
@@ -5,7 +5,7 @@ ext {
     appName = 'Tachiyomi: NHentai'
     pkgNameSuffix = 'all.nhentai'
     extClass = '.NHFactory'
-    extVersionCode = 17
+    extVersionCode = 18
     libVersion = '1.2'
 }
 
diff --git a/src/all/nhentai/src/eu/kanade/tachiyomi/extension/all/nhentai/NHUtils.kt b/src/all/nhentai/src/eu/kanade/tachiyomi/extension/all/nhentai/NHUtils.kt
index 67fce181e..69d81cbe2 100644
--- a/src/all/nhentai/src/eu/kanade/tachiyomi/extension/all/nhentai/NHUtils.kt
+++ b/src/all/nhentai/src/eu/kanade/tachiyomi/extension/all/nhentai/NHUtils.kt
@@ -1,90 +1,53 @@
 package eu.kanade.tachiyomi.extension.all.nhentai
 
 import org.jsoup.nodes.Document
+import org.jsoup.nodes.Element
 import java.text.SimpleDateFormat
 
-class NHUtils {
-    companion object {
-        fun getArtists(document: Document): String {
-            val stringBuilder = StringBuilder()
-            val artists = document.select("#tags > div:nth-child(4) > span > a")
-
-            artists.forEach {
-                stringBuilder.append(cleanTag(it.text()))
-
-                if (it != artists.last())
-                    stringBuilder.append(", ")
-            }
-
-            return stringBuilder.toString()
-        }
-
-        fun getGroups(document: Document): String? {
-            val stringBuilder = StringBuilder()
-            val groups = document.select("#tags > div:nth-child(5) > span > a")
-
-            groups.forEach {
-                stringBuilder.append(cleanTag(it.text()))
-
-                if (it != groups.last())
-                    stringBuilder.append(", ")
-            }
-
-            return if (stringBuilder.toString().isEmpty()) null else stringBuilder.toString()
-        }
-
-        fun getTags(document: Document): String {
-            val stringBuilder = StringBuilder()
-            val parodies = document.select("#tags > div:nth-child(1) > span > a")
-            val characters = document.select("#tags > div:nth-child(2) > span > a")
-            val tags = document.select("#tags > div:nth-child(3) > span > a")
-
-            if (parodies.size > 0) {
-                stringBuilder.append("Parodies: ")
-
-                parodies.forEach {
-                    stringBuilder.append(cleanTag(it.text()))
-
-                    if (it != parodies.last())
-                        stringBuilder.append(", ")
-                }
-
-                stringBuilder.append("\n\n")
-            }
-
-            if (characters.size > 0) {
-                stringBuilder.append("Characters: ")
-
-                characters.forEach {
-                    stringBuilder.append(cleanTag(it.text()))
-
-                    if (it != characters.last())
-                        stringBuilder.append(", ")
-                }
-
-                stringBuilder.append("\n\n")
-            }
-
-            if (tags.size > 0) {
-                stringBuilder.append("Tags: ")
-
-                tags.forEach {
-                    stringBuilder.append(cleanTag(it.text()))
-
-                    if (it != tags.last())
-                        stringBuilder.append(", ")
-                }
-            }
-
-            return stringBuilder.toString()
-        }
-
-        fun getTime(document: Document): Long {
-            val timeString = document.toString().substringAfter("datetime=\"").substringBefore("\">").replace("T", " ")
-
-            return SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSSZ").parse(timeString).time
-        }
-
-        private fun cleanTag(tag: String): String = tag.replace(Regex("\\(.*\\)"), "").trim()
+object NHUtils {
+    fun getArtists(document: Document): String {
+        val artists = document.select("#tags > div:nth-child(4) > span > a")
+        return artists.joinToString(", ") { it.cleanTag() }
     }
+
+    fun getGroups(document: Document): String? {
+        val groups = document.select("#tags > div:nth-child(5) > span > a")
+        return if (groups.isNotEmpty()) {
+            groups.joinToString(", ") { it.cleanTag() }
+        } else {
+            null
+        }
+    }
+
+    fun getTagDescription(document: Document): String {
+        val stringBuilder = StringBuilder()
+
+        val parodies = document.select("#tags > div:nth-child(1) > span > a")
+        if (parodies.isNotEmpty()) {
+            stringBuilder.append("Parodies: ")
+            stringBuilder.append(parodies.joinToString(", ") { it.cleanTag() })
+            stringBuilder.append("\n\n")
+        }
+
+        val characters = document.select("#tags > div:nth-child(2) > span > a")
+        if (characters.isNotEmpty()) {
+            stringBuilder.append("Characters: ")
+            stringBuilder.append(characters.joinToString(", ") { it.cleanTag() })
+        }
+
+        return stringBuilder.toString()
+    }
+
+    fun getTags(document: Document): String {
+        val tags = document.select("#tags > div:nth-child(3) > span > a")
+        return tags.map { it.cleanTag() }.sorted().joinToString(", ")
+    }
+
+    fun getTime(document: Document): Long {
+        val timeString = document.toString().substringAfter("datetime=\"").substringBefore("\">").replace("T", " ")
+
+        return SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSSZ").parse(timeString).time
+    }
+
+    private fun Element.cleanTag(): String = text().replace(Regex("\\(.*\\)"), "").trim()
 }
diff --git a/src/all/nhentai/src/eu/kanade/tachiyomi/extension/all/nhentai/NHentai.kt b/src/all/nhentai/src/eu/kanade/tachiyomi/extension/all/nhentai/NHentai.kt
index f7a8edd78..a66363c40 100644
--- a/src/all/nhentai/src/eu/kanade/tachiyomi/extension/all/nhentai/NHentai.kt
+++ b/src/all/nhentai/src/eu/kanade/tachiyomi/extension/all/nhentai/NHentai.kt
@@ -5,10 +5,11 @@ import android.content.SharedPreferences
 import android.support.v7.preference.ListPreference
 import android.support.v7.preference.PreferenceScreen
 import eu.kanade.tachiyomi.extension.BuildConfig
-import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.Companion.getArtists
-import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.Companion.getGroups
-import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.Companion.getTags
-import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.Companion.getTime
+import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getArtists
+import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getGroups
+import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getTagDescription
+import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getTags
+import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getTime
 import eu.kanade.tachiyomi.network.GET
 import eu.kanade.tachiyomi.network.asObservableSuccess
 import eu.kanade.tachiyomi.source.ConfigurableSource
@@ -139,16 +140,24 @@ open class NHentai(
     }
 
     override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
-        val url = HttpUrl.parse("$baseUrl/search")!!.newBuilder()
-            .addQueryParameter("q", "$query +$nhLang")
-            .addQueryParameter("page", page.toString())
+        val favoriteFilter = filters.findInstance<FavoriteFilter>()
+        if (favoriteFilter != null && favoriteFilter.state) {
+            val url = HttpUrl.parse("$baseUrl/favorites")!!.newBuilder()
+                .addQueryParameter("q", query)
+                .addQueryParameter("page", page.toString())
 
-        for (filter in if (filters.isEmpty()) getFilterList() else filters) {
-            when (filter) {
-                is SortFilter -> url.addQueryParameter("sort", filter.values[filter.state].toLowerCase())
+            return GET(url.toString(), headers)
+        } else {
+            val url = HttpUrl.parse("$baseUrl/search")!!.newBuilder()
+                .addQueryParameter("q", "$query +$nhLang")
+                .addQueryParameter("page", page.toString())
+
+            filters.findInstance<SortFilter>()?.let { f ->
+                url.addQueryParameter("sort", f.values[f.state].toLowerCase())
             }
+
+            return GET(url.toString(), headers)
         }
-        return GET(url.toString(), headers)
     }
 
     private fun searchMangaByIdRequest(id: String) = GET("$baseUrl/g/$id", headers)
@@ -159,6 +168,17 @@ open class NHentai(
         return MangasPage(listOf(details), false)
     }
 
+    override fun searchMangaParse(response: Response): MangasPage {
+        if (response.request().url().toString().contains("/login/")) {
+            val document = response.asJsoup()
+            if (document.select(".fa-sign-in").isNotEmpty()) {
+                throw Exception("Log in via WebView to view favorites")
+            }
+        }
+
+        return super.searchMangaParse(response)
+    }
+
     override fun searchMangaFromElement(element: Element) = latestUpdatesFromElement(element)
 
     override fun searchMangaSelector() = latestUpdatesSelector()
@@ -181,7 +201,8 @@ open class NHentai(
                 .plus("Length: ${document.select("div#info div:contains(pages)").text()}\n")
                 .plus("Favorited by: ${document.select("div#info i.fa-heart + span span").text().removeSurrounding("(", ")")}\n")
                 .plus("Categories: ${document.select("div.field-name:contains(Categories) span.tags a").first()?.ownText()}\n\n")
-                .plus(getTags(document))
+                .plus(getTagDescription(document))
+            genre = getTags(document)
         }
     }
 
@@ -218,15 +239,23 @@ open class NHentai(
         return pageList
     }
 
-    override fun getFilterList(): FilterList = FilterList(SortFilter())
+    override fun getFilterList(): FilterList = FilterList(
+        SortFilter(),
+        Filter.Header("Sort is ignored if favorites only"),
+        FavoriteFilter()
+    )
 
     override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used")
 
+    private class FavoriteFilter : Filter.CheckBox("Show favorites only", false)
+
+    private class SortFilter : Filter.Select<String>("Sort", arrayOf("Popular", "Date"))
+
+    private inline fun <reified T> Iterable<*>.findInstance() = find { it is T } as? T
+
     companion object {
         const val PREFIX_ID_SEARCH = "id:"
         private const val TITLE_PREF = "Display manga title as:"
     }
 
-    private class SortFilter : Filter.Select<String>("Sort", arrayOf("Popular", "Date"))
-
 }