package exh import android.net.Uri import com.elvishew.xlog.XLog import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.all.EHentai import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import uy.kohesive.injekt.injectLazy class GalleryAdder { private val db: DatabaseHelper by injectLazy() private val sourceManager: SourceManager by injectLazy() fun addGallery(url: String, fav: Boolean = false, forceSource: UrlImportableSource? = null, throttleFunc: () -> Unit = {}): GalleryAddEvent { XLog.d("Importing gallery (url: %s, fav: %s, forceSource: %s)...", url, fav, forceSource) try { val uri = Uri.parse(url) // Find matching source val source = if(forceSource != null) { try { if (forceSource.matchesUri(uri)) forceSource else return GalleryAddEvent.Fail.UnknownType(url) } catch(e: Exception) { XLog.e("Source URI match check error!", e) return GalleryAddEvent.Fail.UnknownType(url) } } else { sourceManager.getVisibleCatalogueSources() .filterIsInstance() .find { try { it.matchesUri(uri) } catch(e: Exception) { XLog.e("Source URI match check error!", e) false } } ?: return GalleryAddEvent.Fail.UnknownType(url) } // Map URL to manga URL val realUrl = try { source.mapUrlToMangaUrl(uri) } catch(e: Exception) { XLog.e("Source URI map-to-manga error!", e) null } ?: return GalleryAddEvent.Fail.UnknownType(url) // Clean URL val cleanedUrl = try { source.cleanMangaUrl(realUrl) } catch(e: Exception) { XLog.e("Source URI clean error!", e) null } ?: return GalleryAddEvent.Fail.UnknownType(url) //Use manga in DB if possible, otherwise, make a new manga val manga = db.getManga(cleanedUrl, source.id).executeAsBlocking() ?: Manga.create(source.id).apply { this.url = cleanedUrl title = realUrl } // Insert created manga if not in DB before fetching details // This allows us to keep the metadata when fetching details if(manga.id == null) { db.insertManga(manga).executeAsBlocking().insertedId()?.let { manga.id = it } } // Fetch and copy details val newManga = source.fetchMangaDetails(manga).toBlocking().first() manga.copyFrom(newManga) manga.initialized = true if (fav) manga.favorite = true db.insertManga(manga).executeAsBlocking() //Fetch and copy chapters try { val chapterListObs = if(source is EHentai) { source.fetchChapterList(manga, throttleFunc) } else { source.fetchChapterList(manga) } chapterListObs.map { syncChaptersWithSource(db, it, manga, source) }.toBlocking().first() } catch (e: Exception) { XLog.w("Failed to update chapters for gallery: ${manga.title}!", e) return GalleryAddEvent.Fail.Error(url, "Failed to update chapters for gallery: $url") } return GalleryAddEvent.Success(url, manga) } catch(e: Exception) { XLog.w("Could not add gallery (url: $url)!", e) if(e is EHentai.GalleryNotFoundException) { return GalleryAddEvent.Fail.NotFound(url) } return GalleryAddEvent.Fail.Error(url, ((e.message ?: "Unknown error!") + " (Gallery: $url)").trim()) } } } sealed class GalleryAddEvent { abstract val logMessage: String abstract val galleryUrl: String open val galleryTitle: String? = null class Success(override val galleryUrl: String, val manga: Manga): GalleryAddEvent() { override val galleryTitle = manga.title override val logMessage = "Added gallery: $galleryTitle" } sealed class Fail: GalleryAddEvent() { class UnknownType(override val galleryUrl: String): Fail() { override val logMessage = "Unknown gallery type for gallery: $galleryUrl" } open class Error(override val galleryUrl: String, override val logMessage: String): Fail() class NotFound(galleryUrl: String): Error(galleryUrl, "Gallery does not exist: $galleryUrl") } }