@ -31,17 +31,19 @@ import java.util.zip.ZipFile
class LocalSource ( private val context : Context ) : CatalogueSource {
class LocalSource ( private val context : Context ) : CatalogueSource {
companion object {
companion object {
const val ID = 0L
const val HELP _URL = " https://tachiyomi.org/help/guides/reading-local-manga/ "
const val HELP _URL = " https://tachiyomi.org/help/guides/reading-local-manga/ "
private const val COVER _NAME = " cover.jpg "
private const val COVER _NAME = " cover.jpg "
private val SUPPORTED _ARCHIVE _TYPES = setOf ( " zip " , " rar " , " cbr " , " cbz " , " epub " )
private val POPULAR _FILTERS = FilterList ( OrderBy ( ) )
private val POPULAR _FILTERS = FilterList ( OrderBy ( ) )
private val LATEST _FILTERS =
private val LATEST _FILTERS =
FilterList ( OrderBy ( ) . apply { state = Filter . Sort . Selection ( 1 , false ) } )
FilterList ( OrderBy ( ) . apply { state = Filter . Sort . Selection ( 1 , false ) } )
private val LATEST _THRESHOLD = TimeUnit . MILLISECONDS . convert ( 7 , TimeUnit . DAYS )
private val LATEST _THRESHOLD = TimeUnit . MILLISECONDS . convert ( 7 , TimeUnit . DAYS )
const val ID = 0L
fun updateCover ( context : Context , manga : SManga , input : InputStream ) : File ? {
fun updateCover ( context : Context , manga : SManga , input : InputStream ) : File ? {
val dir = getBaseDirectories ( context ) . firstOrNull( )
val dir = getBaseDirectories ( context ) . asSequence( ) . firstOrNull( )
if ( dir == null ) {
if ( dir == null ) {
input . close ( )
input . close ( )
return null
return null
@ -85,12 +87,12 @@ class LocalSource(private val context: Context) : CatalogueSource {
val time =
val time =
if ( filters === LATEST _FILTERS ) System . currentTimeMillis ( ) - LATEST _THRESHOLD else 0L
if ( filters === LATEST _FILTERS ) System . currentTimeMillis ( ) - LATEST _THRESHOLD else 0L
var mangaDirs = baseDirs . mapNotNull { it . listFiles ( ) ?. toList ( ) } . flatten ( ) . filter {
var mangaDirs = baseDirs
it . isDirectory && if ( time == 0L ) it . name . contains (
. asSequence ( )
query ,
. mapNotNull { it . listFiles ( ) ?. toList ( ) } . flatten ( )
ignoreCase = true
. filter { it . isDirectory }
) else it . lastModified ( ) >= time
. filter { if ( time == 0L ) it . name . contains ( query , ignoreCase = true ) else it . lastModified ( ) >= time }
} .distinctBy { it . name }
.distinctBy { it . name }
val state = ( ( if ( filters . isEmpty ( ) ) POPULAR _FILTERS else filters ) [ 0 ] as OrderBy ) . state
val state = ( ( if ( filters . isEmpty ( ) ) POPULAR _FILTERS else filters ) [ 0 ] as OrderBy ) . state
when ( state ?. index ) {
when ( state ?. index ) {
@ -134,15 +136,17 @@ class LocalSource(private val context: Context) : CatalogueSource {
}
}
}
}
}
}
return Observable . just ( MangasPage ( mangas , false ) )
return Observable . just ( MangasPage ( mangas .toList ( ) , false ) )
}
}
override fun fetchLatestUpdates ( page : Int ) = fetchSearchManga ( page , " " , LATEST _FILTERS )
override fun fetchLatestUpdates ( page : Int ) = fetchSearchManga ( page , " " , LATEST _FILTERS )
override fun fetchMangaDetails ( manga : SManga ) : Observable < SManga > {
override fun fetchMangaDetails ( manga : SManga ) : Observable < SManga > {
val baseDirs = getBaseDirectories ( context )
val baseDirs = getBaseDirectories ( context )
baseDirs . mapNotNull { File ( it , manga . url ) . listFiles ( ) ?. toList ( ) }
baseDirs
. flatten ( ) . filter { it . extension == " json " } . firstOrNull ( ) ?. apply {
. mapNotNull { File ( it , manga . url ) . listFiles ( ) ?. toList ( ) }
. flatten ( )
. filter { it . extension == " json " } . firstOrNull ( ) ?. apply {
val json = Gson ( ) . fromJson (
val json = Gson ( ) . fromJson (
Scanner ( this ) . useDelimiter ( " \\ Z " ) . next ( ) ,
Scanner ( this ) . useDelimiter ( " \\ Z " ) . next ( ) ,
JsonObject :: class . java
JsonObject :: class . java
@ -155,6 +159,7 @@ class LocalSource(private val context: Context) : CatalogueSource {
?: manga . genre
?: manga . genre
manga . status = json [ " status " ] ?. asInt ?: manga . status
manga . status = json [ " status " ] ?. asInt ?: manga . status
}
}
val url = manga . url
val url = manga . url
// Try to find the cover
// Try to find the cover
for ( dir in baseDirs ) {
for ( dir in baseDirs ) {
@ -249,7 +254,7 @@ class LocalSource(private val context: Context) : CatalogueSource {
}
}
private fun isSupportedFile ( extension : String ) : Boolean {
private fun isSupportedFile ( extension : String ) : Boolean {
return extension . toLowerCase ( ) in setOf( " zip " , " rar " , " cbr " , " cbz " , " epub " )
return extension . toLowerCase ( ) in SUPPORTED_ARCHIVE _TYPES
}
}
fun getFormat ( chapter : SChapter ) : Format {
fun getFormat ( chapter : SChapter ) : Format {
@ -283,22 +288,23 @@ class LocalSource(private val context: Context) : CatalogueSource {
val format = getFormat ( chapter )
val format = getFormat ( chapter )
return when ( format ) {
return when ( format ) {
is Format . Directory -> {
is Format . Directory -> {
val entry = format . file . listFiles ( ) . sortedWith ( Comparator < File > { f1 , f2 ->
val entry = format . file . listFiles ( )
?. sortedWith ( Comparator < File > { f1 , f2 ->
f1 . name . compareToCaseInsensitiveNaturalOrder ( f2 . name )
f1 . name . compareToCaseInsensitiveNaturalOrder ( f2 . name )
} )
} )
.find { ! it . isDirectory && ImageUtil . isImage ( it . name ) { FileInputStream ( it ) } }
? .find { ! it . isDirectory && ImageUtil . isImage ( it . name ) { FileInputStream ( it ) } }
entry ?. let { updateCover ( context , manga , it . inputStream ( ) ) }
entry ?. let { updateCover ( context , manga , it . inputStream ( ) ) }
}
}
is Format . Zip -> {
is Format . Zip -> {
ZipFile ( format . file ) . use { zip ->
ZipFile ( format . file ) . use { zip ->
val entry = zip . entries ( ) . toList ( ) . sortedWith ( Comparator < ZipEntry > { f1 , f2 ->
val entry = zip . entries ( ) . toList ( ) . sortedWith ( Comparator < ZipEntry > { f1 , f2 ->
f1 . name . compareToCaseInsensitiveNaturalOrder ( f2 . name )
f1 . name . compareToCaseInsensitiveNaturalOrder ( f2 . name )
} ) . find {
} ) . find {
! it . isDirectory && ImageUtil . isImage ( it . name ) {
! it . isDirectory && ImageUtil . isImage ( it . name ) {
zip . getInputStream ( it )
zip . getInputStream ( it )
}
}
}
}
entry ?. let { updateCover ( context , manga , zip . getInputStream ( it ) ) }
entry ?. let { updateCover ( context , manga , zip . getInputStream ( it ) ) }
}
}
@ -306,12 +312,12 @@ class LocalSource(private val context: Context) : CatalogueSource {
is Format . Rar -> {
is Format . Rar -> {
Archive ( format . file ) . use { archive ->
Archive ( format . file ) . use { archive ->
val entry = archive . fileHeaders . sortedWith ( Comparator < FileHeader > { f1 , f2 ->
val entry = archive . fileHeaders . sortedWith ( Comparator < FileHeader > { f1 , f2 ->
f1 . fileNameString . compareToCaseInsensitiveNaturalOrder ( f2 . fileNameString )
f1 . fileNameString . compareToCaseInsensitiveNaturalOrder ( f2 . fileNameString )
} ) . find {
} ) . find {
! it . isDirectory && ImageUtil . isImage ( it . fileNameString ) {
! it . isDirectory && ImageUtil . isImage ( it . fileNameString ) {
archive . getInputStream ( it )
archive . getInputStream ( it )
}
}
}
}
entry ?. let { updateCover ( context , manga , archive . getInputStream ( it ) ) }
entry ?. let { updateCover ( context , manga , archive . getInputStream ( it ) ) }
}
}