|  |  | @ -15,10 +15,15 @@ import kotlinx.coroutines.Dispatchers.IO | 
			
		
	
		
		
			
				
					
					|  |  |  | import kotlinx.coroutines.Dispatchers.Main |  |  |  | import kotlinx.coroutines.Dispatchers.Main | 
			
		
	
		
		
			
				
					
					|  |  |  | import kotlinx.coroutines.delay |  |  |  | import kotlinx.coroutines.delay | 
			
		
	
		
		
			
				
					
					|  |  |  | import kotlinx.coroutines.withContext |  |  |  | import kotlinx.coroutines.withContext | 
			
		
	
		
		
			
				
					
					|  |  |  | import org.apache.commons.text.similarity.JaccardSimilarity |  |  |  | import net.ricecode.similarity.JaroWinklerStrategy | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import net.ricecode.similarity.StringSimilarityServiceImpl | 
			
		
	
		
		
			
				
					
					|  |  |  | import org.jetbrains.anko.toast |  |  |  | import org.jetbrains.anko.toast | 
			
		
	
		
		
			
				
					
					|  |  |  | import java.io.File |  |  |  | import java.io.File | 
			
		
	
		
		
			
				
					
					|  |  |  | import java.util.concurrent.CopyOnWriteArraySet |  |  |  | import java.util.concurrent.CopyOnWriteArraySet | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import java.util.regex.Matcher | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import java.util.regex.Pattern | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import kotlin.math.abs | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import kotlin.math.max | 
			
		
	
		
		
			
				
					
					|  |  |  | import kotlin.math.min |  |  |  | import kotlin.math.min | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | object BookHelp { |  |  |  | object BookHelp { | 
			
		
	
	
		
		
			
				
					|  |  | @ -202,54 +207,88 @@ object BookHelp { | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     /** |  |  |  |     /** | 
			
		
	
		
		
			
				
					
					|  |  |  |      * 找到相似度最高的章节 |  |  |  |      * 根据目录名获取当前章节 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |      */ |  |  |  |      */ | 
			
		
	
		
		
			
				
					
					|  |  |  |     fun getDurChapterIndexByChapterTitle( |  |  |  |     fun getDurChapter( | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         title: String?, |  |  |  |         oldDurChapterIndex: Int, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         index: Int, |  |  |  |         oldChapterListSize: Int, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         chapters: List<BookChapter>, |  |  |  |         oldDurChapterName: String?, | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         newChapterList: List<BookChapter> | 
			
		
	
		
		
			
				
					
					|  |  |  |     ): Int { |  |  |  |     ): Int { | 
			
		
	
		
		
			
				
					
					|  |  |  |         if (title.isNullOrEmpty()) { |  |  |  |         if (oldChapterListSize == 0) return 0 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             return min(index, chapters.lastIndex) |  |  |  |         val oldChapterNum = getChapterNum(oldDurChapterName) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         val oldName = getPureChapterName(oldDurChapterName) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         if (chapters.size > index && title == chapters[index].title) { |  |  |  |         val newChapterSize = newChapterList.size | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             return index |  |  |  |         val min = max( | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |             0, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |             min( | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 oldDurChapterIndex, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 oldDurChapterIndex - oldChapterListSize + newChapterSize | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             ) - 10 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         ) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         val max = min( | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             newChapterSize - 1, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             max( | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 oldDurChapterIndex, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 oldDurChapterIndex - oldChapterListSize + newChapterSize | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             ) + 10 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         ) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         var nameSim = 0.0 | 
			
		
	
		
		
			
				
					
					|  |  |  |         var newIndex = 0 |  |  |  |         var newIndex = 0 | 
			
		
	
		
		
			
				
					
					|  |  |  |         val jSimilarity = JaccardSimilarity() |  |  |  |         var newNum = 0 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         var similarity = if (chapters.size > index) { |  |  |  |         if (oldName.isNotEmpty()) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             jSimilarity.apply(title, chapters[index].title) |  |  |  |             val service = StringSimilarityServiceImpl(JaroWinklerStrategy()) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         } else 0.0 |  |  |  |             for (i in min..max) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         if (similarity == 1.0) { |  |  |  |                 val newName = getPureChapterName(newChapterList[i].title) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             return index |  |  |  |                 val temp = service.score(oldName, newName) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         } else { |  |  |  |                 if (temp > nameSim) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             for (i in 1..50) { |  |  |  |                     nameSim = temp | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 if (index - i in chapters.indices) { |  |  |  |                     newIndex = i | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                     jSimilarity.apply(title, chapters[index - i].title).let { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                         if (it > similarity) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                             similarity = it |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                             newIndex = index - i |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                             if (similarity == 1.0) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                                 return newIndex |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                             } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                         } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     } |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |                 } |  |  |  |                 } | 
			
		
	
		
		
			
				
					
					|  |  |  |                 if (index + i in chapters.indices) { |  |  |  |             } | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                     jSimilarity.apply(title, chapters[index + i].title).let { |  |  |  |         } | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                         if (it > similarity) { |  |  |  |         if (nameSim < 0.96 && oldChapterNum > 0) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             similarity = it |  |  |  |             for (i in min..max) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             newIndex = index + i |  |  |  |                 val temp = getChapterNum(newChapterList[i].title) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             if (similarity == 1.0) { |  |  |  |                 if (temp == oldChapterNum) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                                 return newIndex |  |  |  |                     newNum = temp | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             } |  |  |  |                     newIndex = i | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                         } |  |  |  |                     break | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                     } |  |  |  |                 } else if (abs(temp - oldChapterNum) < abs(newNum - oldChapterNum)) { | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     newNum = temp | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     newIndex = i | 
			
		
	
		
		
			
				
					
					|  |  |  |                 } |  |  |  |                 } | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |         return newIndex |  |  |  |         return if (nameSim > 0.96 || abs(newNum - oldChapterNum) < 1) { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             newIndex | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         } else { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             min(max(0, newChapterList.size - 1), oldDurChapterIndex) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     private val chapterNamePattern = | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         Pattern.compile("^(.*?第([\\d零〇一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟0-9\\s]+)[章节篇回集])[、,。 ::.\\s]*") | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     private fun getChapterNum(chapterName: String?): Int { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         if (chapterName != null) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             val matcher: Matcher = chapterNamePattern.matcher(chapterName) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             if (matcher.find()) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 return StringUtils.stringToInt(matcher.group(2)) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         return -1 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     private fun getPureChapterName(chapterName: String?): String { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         // 所有非字母数字中日韩文字 CJK区+扩展A-F区 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         return if (chapterName == null) "" else StringUtils.fullToHalf(chapterName) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             .replace("\\s".toRegex(), "") | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             .replace("^第.*?章|[(\\[][^()\\[\\]]{2,}[)\\]]$".toRegex(), "") | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             .replace( | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 "[^\\w\\u4E00-\\u9FEF〇\\u3400-\\u4DBF\\u20000-\\u2A6DF\\u2A700-\\u2EBEF]".toRegex(), | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 "" | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             ) | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     private var bookName: String? = null |  |  |  |     private var bookName: String? = null | 
			
		
	
	
		
		
			
				
					|  |  | 
 |