diff --git a/app/build.gradle b/app/build.gradle
index 2b5855fda..bd56ede7c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -34,6 +34,10 @@ android {
     buildToolsVersion "25.0.1"
     publishNonDefault true
 
+    dexOptions {
+        javaMaxHeapSize "4g"
+    }
+
     defaultConfig {
         applicationId "eu.kanade.tachiyomi.eh2"
         minSdkVersion 16
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt
index 62804132d..c4deaecc9 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt
@@ -166,6 +166,8 @@ class PreferencesHelper(val context: Context) {
 
     fun finishMainActivity() = rxPrefs.getBoolean("finish_main_activity", false)
 
+    fun hasPerformedURLMigration() = rxPrefs.getBoolean("performed_url_migration", false)
+
     //EH Cookies
     fun memberIdVal() = rxPrefs.getString("eh_ipb_member_id", null)
     fun passHashVal() = rxPrefs.getString("eh_ipb_pass_hash", null)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/backup/BackupPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/backup/BackupPresenter.kt
index 7ae7c7a58..64b311b4c 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/backup/BackupPresenter.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/backup/BackupPresenter.kt
@@ -4,6 +4,7 @@ import android.os.Bundle
 import eu.kanade.tachiyomi.data.backup.BackupManager
 import eu.kanade.tachiyomi.data.database.DatabaseHelper
 import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
+import exh.ui.migration.UrlMigrator
 import rx.Observable
 import rx.Subscription
 import rx.android.schedulers.AndroidSchedulers
@@ -87,6 +88,7 @@ class BackupPresenter : BasePresenter<BackupFragment>() {
      */
     private fun getRestoreObservable(stream: InputStream) = Observable.fromCallable {
         backupManager.restoreFromStream(stream)
+        UrlMigrator().perform() //Clean up URLs
         true
     }
 
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
index 8af344ba9..ee40ba579 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
@@ -20,6 +20,7 @@ import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadFragment
 import eu.kanade.tachiyomi.ui.setting.SettingsActivity
 import exh.ui.batchadd.BatchAddFragment
 import exh.ui.migration.LibraryMigrationManager
+import exh.ui.migration.UrlMigrator
 import kotlinx.android.synthetic.main.activity_main.*
 import kotlinx.android.synthetic.main.toolbar.*
 import rx.Subscription
@@ -100,6 +101,9 @@ class MainActivity : BaseActivity() {
                 if(it)
                     finish()
             }
+
+            //Migrate URLs if necessary
+            UrlMigrator().tryMigration()
         }
     }
 
diff --git a/app/src/main/java/exh/ui/migration/MigrationCompletionActivity.kt b/app/src/main/java/exh/ui/migration/MigrationCompletionActivity.kt
index f2571835a..70fe14cac 100644
--- a/app/src/main/java/exh/ui/migration/MigrationCompletionActivity.kt
+++ b/app/src/main/java/exh/ui/migration/MigrationCompletionActivity.kt
@@ -40,7 +40,6 @@ class MigrationCompletionActivity : BaseActivity() {
     fun setup() {
         try {
             val sc = ShareCompat.IntentReader.from(this)
-            Timber.i("CP: " + sc.callingPackage)
             if(sc.isShareIntent) {
                 //Try to restore backup
                 thread {
@@ -54,6 +53,9 @@ class MigrationCompletionActivity : BaseActivity() {
                         return@thread
                     }
 
+                    //Migrate urls
+                    UrlMigrator().perform()
+
                     //Go back to MainActivity
                     //Set final steps
                     preferenceManager.migrationStatus().set(MigrationStatus.FINALIZE_MIGRATION)
diff --git a/app/src/main/java/exh/ui/migration/UrlMigrator.kt b/app/src/main/java/exh/ui/migration/UrlMigrator.kt
new file mode 100644
index 000000000..55ccf373a
--- /dev/null
+++ b/app/src/main/java/exh/ui/migration/UrlMigrator.kt
@@ -0,0 +1,79 @@
+package exh.ui.migration
+
+import eu.kanade.tachiyomi.data.database.DatabaseHelper
+import eu.kanade.tachiyomi.data.database.models.Manga
+import eu.kanade.tachiyomi.data.preference.PreferencesHelper
+import eu.kanade.tachiyomi.data.preference.getOrDefault
+import exh.metadata.MetadataHelper
+import uy.kohesive.injekt.injectLazy
+
+class UrlMigrator {
+    private val db: DatabaseHelper by injectLazy()
+
+    private val prefs: PreferencesHelper by injectLazy()
+
+    private val metadataHelper: MetadataHelper by lazy { MetadataHelper() }
+
+    fun perform() {
+        db.inTransaction {
+            val dbMangas = db.getMangas()
+                    .executeAsBlocking()
+
+            //Find all EX mangas
+            val qualifyingMangas = dbMangas.asSequence().filter {
+                it.source > 0
+                        && it.source <= 4
+            }
+
+            val possibleDups = mutableListOf<Manga>()
+            val badMangas = mutableListOf<Manga>()
+
+            qualifyingMangas.forEach {
+                if(it.url.startsWith("g/")) //Missing slash at front so we are bad
+                    badMangas.add(it)
+                else
+                    possibleDups.add(it)
+            }
+
+            //Sort possible dups so we can use binary search on it
+            possibleDups.sortBy { it.url }
+
+            badMangas.forEach {
+                //Build fixed URL
+                val urlWithSlash = "/" + it.url
+                //Fix metadata if required
+                val metadata = metadataHelper.fetchMetadata(it.url, it.source == 2
+                        || it.source == 4)
+                metadata?.url?.let {
+                    if(it.startsWith("g/")) { //Check if metadata URL has no slash
+                        metadata.url = urlWithSlash //Fix it
+                        metadataHelper.writeGallery(metadata) //Write new metadata to disk
+                    }
+                }
+                //If we have a dup (with the fixed url), use the dup instead
+                val possibleDup = possibleDups.binarySearchBy(urlWithSlash, selector = { it.url })
+                if(possibleDup >= 0) {
+                    //Make sure it is favorited if we are
+                    if(it.favorite) {
+                        val dup = possibleDups[possibleDup]
+                        dup.favorite = true
+                        db.insertManga(dup).executeAsBlocking() //Update DB with changes
+                    }
+                    //Delete ourself (but the dup is still there)
+                    db.deleteManga(it).executeAsBlocking()
+                    return@forEach
+                }
+                //No dup, correct URL and reinsert ourselves
+                it.url = urlWithSlash
+                db.insertManga(it).executeAsBlocking()
+            }
+        }
+    }
+
+    fun tryMigration() {
+        if(!prefs.hasPerformedURLMigration().getOrDefault()) {
+            perform()
+            prefs.hasPerformedURLMigration().set(true)
+        }
+    }
+}