This commit is contained in:
Horis
2024-07-08 08:48:04 +08:00
parent f86bd1d507
commit e19f8c4568
7 changed files with 91 additions and 48 deletions
@@ -5,6 +5,7 @@ import android.webkit.WebSettings
import androidx.annotation.Keep import androidx.annotation.Keep
import cn.hutool.core.codec.Base64 import cn.hutool.core.codec.Base64
import cn.hutool.core.util.HexUtil import cn.hutool.core.util.HexUtil
import com.script.rhino.RhinoContext
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.AppConst.dateFormat import io.legado.app.constant.AppConst.dateFormat
import io.legado.app.constant.AppLog import io.legado.app.constant.AppLog
@@ -50,6 +51,7 @@ import kotlinx.coroutines.runBlocking
import okio.use import okio.use
import org.jsoup.Connection import org.jsoup.Connection
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.mozilla.javascript.Context
import splitties.init.appCtx import splitties.init.appCtx
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
@@ -65,6 +67,8 @@ import java.util.SimpleTimeZone
import java.util.UUID import java.util.UUID
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
/** /**
* js扩展类, 在js中通过java变量调用 * js扩展类, 在js中通过java变量调用
@@ -78,6 +82,12 @@ interface JsExtensions : JsEncodeUtils {
fun getSource(): BaseSource? fun getSource(): BaseSource?
private val context: CoroutineContext
get() {
val rhinoContext = Context.getCurrentContext() as RhinoContext
return rhinoContext.coroutineContext ?: EmptyCoroutineContext
}
/** /**
* 访问网络,返回String * 访问网络,返回String
*/ */
@@ -87,9 +97,9 @@ interface JsExtensions : JsEncodeUtils {
} else { } else {
url.toString() url.toString()
} }
return runBlocking { val analyzeUrl = AnalyzeUrl(urlStr, source = getSource(), coroutineContext = context)
return runBlocking(context) {
kotlin.runCatching { kotlin.runCatching {
val analyzeUrl = AnalyzeUrl(urlStr, source = getSource())
analyzeUrl.getStrResponseAwait().body analyzeUrl.getStrResponseAwait().body
}.onFailure { }.onFailure {
AppLog.put("ajax(${urlStr}) error\n${it.localizedMessage}", it) AppLog.put("ajax(${urlStr}) error\n${it.localizedMessage}", it)
@@ -103,7 +113,7 @@ interface JsExtensions : JsEncodeUtils {
* 并发访问网络 * 并发访问网络
*/ */
fun ajaxAll(urlList: Array<String>): Array<StrResponse?> { fun ajaxAll(urlList: Array<String>): Array<StrResponse?> {
return runBlocking { return runBlocking(context) {
val asyncArray = Array(urlList.size) { val asyncArray = Array(urlList.size) {
async(IO) { async(IO) {
val url = urlList[it] val url = urlList[it]
@@ -122,7 +132,7 @@ interface JsExtensions : JsEncodeUtils {
* 访问网络,返回Response<String> * 访问网络,返回Response<String>
*/ */
fun connect(urlStr: String): StrResponse { fun connect(urlStr: String): StrResponse {
return runBlocking { return runBlocking(context) {
val analyzeUrl = AnalyzeUrl(urlStr, source = getSource()) val analyzeUrl = AnalyzeUrl(urlStr, source = getSource())
kotlin.runCatching { kotlin.runCatching {
analyzeUrl.getStrResponseAwait() analyzeUrl.getStrResponseAwait()
@@ -135,7 +145,7 @@ interface JsExtensions : JsEncodeUtils {
} }
fun connect(urlStr: String, header: String?): StrResponse { fun connect(urlStr: String, header: String?): StrResponse {
return runBlocking { return runBlocking(context) {
val headerMap = GSON.fromJsonObject<Map<String, String>>(header).getOrNull() val headerMap = GSON.fromJsonObject<Map<String, String>>(header).getOrNull()
val analyzeUrl = AnalyzeUrl(urlStr, headerMapF = headerMap, source = getSource()) val analyzeUrl = AnalyzeUrl(urlStr, headerMapF = headerMap, source = getSource())
kotlin.runCatching { kotlin.runCatching {
@@ -156,7 +166,7 @@ interface JsExtensions : JsEncodeUtils {
* @return 返回js获取的内容 * @return 返回js获取的内容
*/ */
fun webView(html: String?, url: String?, js: String?): String? { fun webView(html: String?, url: String?, js: String?): String? {
return runBlocking { return runBlocking(context) {
BackstageWebView( BackstageWebView(
url = url, url = url,
html = html, html = html,
@@ -171,7 +181,7 @@ interface JsExtensions : JsEncodeUtils {
* 使用webView获取资源url * 使用webView获取资源url
*/ */
fun webViewGetSource(html: String?, url: String?, js: String?, sourceRegex: String): String? { fun webViewGetSource(html: String?, url: String?, js: String?, sourceRegex: String): String? {
return runBlocking { return runBlocking(context) {
BackstageWebView( BackstageWebView(
url = url, url = url,
html = html, html = html,
@@ -192,7 +202,7 @@ interface JsExtensions : JsEncodeUtils {
js: String?, js: String?,
overrideUrlRegex: String overrideUrlRegex: String
): String? { ): String? {
return runBlocking { return runBlocking(context) {
BackstageWebView( BackstageWebView(
url = url, url = url,
html = html, html = html,
@@ -294,7 +304,7 @@ interface JsExtensions : JsEncodeUtils {
* @return 下载的文件相对路径 * @return 下载的文件相对路径
*/ */
fun downloadFile(url: String): String { fun downloadFile(url: String): String {
val analyzeUrl = AnalyzeUrl(url, source = getSource()) val analyzeUrl = AnalyzeUrl(url, source = getSource(), coroutineContext = context)
val type = UrlUtil.getSuffix(url, analyzeUrl.type) val type = UrlUtil.getSuffix(url, analyzeUrl.type)
val path = FileUtils.getPath( val path = FileUtils.getPath(
File(FileUtils.getCachePath()), File(FileUtils.getCachePath()),
@@ -321,7 +331,8 @@ interface JsExtensions : JsEncodeUtils {
ReplaceWith("downloadFile(url: String)") ReplaceWith("downloadFile(url: String)")
) )
fun downloadFile(content: String, url: String): String { fun downloadFile(content: String, url: String): String {
val type = AnalyzeUrl(url, source = getSource()).type ?: return "" val type = AnalyzeUrl(url, source = getSource(), coroutineContext = context).type
?: return ""
val path = FileUtils.getPath( val path = FileUtils.getPath(
FileUtils.createFolderIfNotExist(FileUtils.getCachePath()), FileUtils.createFolderIfNotExist(FileUtils.getCachePath()),
"${MD5Utils.md5Encode16(url)}.${type}" "${MD5Utils.md5Encode16(url)}.${type}"
@@ -689,7 +700,7 @@ interface JsExtensions : JsEncodeUtils {
*/ */
fun getZipByteArrayContent(url: String, path: String): ByteArray? { fun getZipByteArrayContent(url: String, path: String): ByteArray? {
val bytes = if (url.isAbsUrl()) { val bytes = if (url.isAbsUrl()) {
AnalyzeUrl(url, source = getSource()).getByteArray() AnalyzeUrl(url, source = getSource(), coroutineContext = context).getByteArray()
} else { } else {
HexUtil.decodeHex(url) HexUtil.decodeHex(url)
} }
@@ -717,7 +728,7 @@ interface JsExtensions : JsEncodeUtils {
*/ */
fun getRarByteArrayContent(url: String, path: String): ByteArray? { fun getRarByteArrayContent(url: String, path: String): ByteArray? {
val bytes = if (url.isAbsUrl()) { val bytes = if (url.isAbsUrl()) {
AnalyzeUrl(url, source = getSource()).getByteArray() AnalyzeUrl(url, source = getSource(), coroutineContext = context).getByteArray()
} else { } else {
HexUtil.decodeHex(url) HexUtil.decodeHex(url)
} }
@@ -735,7 +746,7 @@ interface JsExtensions : JsEncodeUtils {
*/ */
fun get7zByteArrayContent(url: String, path: String): ByteArray? { fun get7zByteArrayContent(url: String, path: String): ByteArray? {
val bytes = if (url.isAbsUrl()) { val bytes = if (url.isAbsUrl()) {
AnalyzeUrl(url, source = getSource()).getByteArray() AnalyzeUrl(url, source = getSource(), coroutineContext = context).getByteArray()
} else { } else {
HexUtil.decodeHex(url) HexUtil.decodeHex(url)
} }
@@ -769,14 +780,20 @@ interface JsExtensions : JsEncodeUtils {
when (data) { when (data) {
is String -> { is String -> {
if (useCache) { if (useCache) {
key = MessageDigest.getInstance("SHA-256").digest(data.toByteArray()).toHexString() key = MessageDigest.getInstance("SHA-256").digest(data.toByteArray())
.toHexString()
qTTF = CacheManager.getQueryTTF(key) qTTF = CacheManager.getQueryTTF(key)
if (qTTF != null) return qTTF if (qTTF != null) return qTTF
} }
val font: ByteArray? = when { val font: ByteArray? = when {
data.isContentScheme() -> Uri.parse(data).readBytes(appCtx) data.isContentScheme() -> Uri.parse(data).readBytes(appCtx)
data.startsWith("/storage") -> File(data).readBytes() data.startsWith("/storage") -> File(data).readBytes()
data.isAbsUrl() -> AnalyzeUrl(data, source = getSource()).getByteArray() data.isAbsUrl() -> AnalyzeUrl(
data,
source = getSource(),
coroutineContext = context
).getByteArray()
else -> base64DecodeToByteArray(data) else -> base64DecodeToByteArray(data)
} }
font ?: return null font ?: return null
@@ -32,7 +32,9 @@ import java.util.regex.Pattern
import kotlin.collections.component1 import kotlin.collections.component1
import kotlin.collections.component2 import kotlin.collections.component2
import kotlin.collections.set import kotlin.collections.set
import kotlin.coroutines.ContinuationInterceptor
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
/** /**
* 解析规则获取结果 * 解析规则获取结果
@@ -67,7 +69,7 @@ class AnalyzeRule(
private val stringRuleCache = hashMapOf<String, List<SourceRule>>() private val stringRuleCache = hashMapOf<String, List<SourceRule>>()
private var coroutineContext: CoroutineContext? = null private var coroutineContext: CoroutineContext = EmptyCoroutineContext
@JvmOverloads @JvmOverloads
fun setContent(content: Any?, baseUrl: String? = null): AnalyzeRule { fun setContent(content: Any?, baseUrl: String? = null): AnalyzeRule {
@@ -84,8 +86,8 @@ class AnalyzeRule(
return this return this
} }
fun setCoroutineContext(context: CoroutineContext?): AnalyzeRule { fun setCoroutineContext(context: CoroutineContext): AnalyzeRule {
coroutineContext = context coroutineContext = context.minusKey(ContinuationInterceptor)
return this return this
} }
@@ -777,9 +779,14 @@ class AnalyzeRule(
} else { } else {
url.toString() url.toString()
} }
return runBlocking { val analyzeUrl = AnalyzeUrl(
urlStr,
source = source,
ruleData = book,
coroutineContext = coroutineContext
)
return runBlocking(coroutineContext) {
kotlin.runCatching { kotlin.runCatching {
val analyzeUrl = AnalyzeUrl(urlStr, source = source, ruleData = book)
analyzeUrl.getStrResponseAwait().body analyzeUrl.getStrResponseAwait().body
}.onFailure { }.onFailure {
log("ajax(${urlStr}) error\n${it.stackTraceToString()}") log("ajax(${urlStr}) error\n${it.stackTraceToString()}")
@@ -797,7 +804,7 @@ class AnalyzeRule(
val bookSource = source as? BookSource val bookSource = source as? BookSource
val book = book as? Book val book = book as? Book
if (bookSource == null || book == null) return if (bookSource == null || book == null) return
runBlocking { runBlocking(coroutineContext) {
withTimeout(1800000) { withTimeout(1800000) {
WebBook.preciseSearchAwait(this, bookSource, book.name, book.author) WebBook.preciseSearchAwait(this, bookSource, book.name, book.author)
.getOrThrow().let { .getOrThrow().let {
@@ -818,7 +825,7 @@ class AnalyzeRule(
val bookSource = source as? BookSource val bookSource = source as? BookSource
val book = book as? Book val book = book as? Book
if (bookSource == null || book == null) return if (bookSource == null || book == null) return
runBlocking { runBlocking(coroutineContext) {
withTimeout(1800000) { withTimeout(1800000) {
WebBook.getBookInfoAwait(bookSource, book) WebBook.getBookInfoAwait(bookSource, book)
} }
@@ -832,7 +839,7 @@ class AnalyzeRule(
val bookSource = source as? BookSource val bookSource = source as? BookSource
val book = book as? Book val book = book as? Book
if (bookSource == null || book == null) return if (bookSource == null || book == null) return
runBlocking { runBlocking(coroutineContext) {
withTimeout(1800000) { withTimeout(1800000) {
WebBook.getBookInfoAwait(bookSource, book) WebBook.getBookInfoAwait(bookSource, book)
} }
@@ -35,6 +35,9 @@ import java.io.InputStream
import java.net.URLEncoder import java.net.URLEncoder
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.regex.Pattern import java.util.regex.Pattern
import kotlin.coroutines.ContinuationInterceptor
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.math.max import kotlin.math.max
/** /**
@@ -55,6 +58,7 @@ class AnalyzeUrl(
private val ruleData: RuleDataInterface? = null, private val ruleData: RuleDataInterface? = null,
private val chapter: BookChapter? = null, private val chapter: BookChapter? = null,
private val readTimeout: Long? = null, private val readTimeout: Long? = null,
private var coroutineContext: CoroutineContext = EmptyCoroutineContext,
headerMapF: Map<String, String>? = null, headerMapF: Map<String, String>? = null,
) : JsExtensions { ) : JsExtensions {
companion object { companion object {
@@ -89,6 +93,7 @@ class AnalyzeUrl(
private set private set
init { init {
coroutineContext = coroutineContext.minusKey(ContinuationInterceptor)
val urlMatcher = paramPattern.matcher(baseUrl) val urlMatcher = paramPattern.matcher(baseUrl)
if (urlMatcher.find()) baseUrl = baseUrl.substring(0, urlMatcher.start()) if (urlMatcher.find()) baseUrl = baseUrl.substring(0, urlMatcher.start())
(headerMapF ?: source?.getHeaderMap(true))?.let { (headerMapF ?: source?.getHeaderMap(true))?.let {
@@ -278,7 +283,7 @@ class AnalyzeUrl(
source?.getShareScope()?.let { source?.getShareScope()?.let {
scope.prototype = it scope.prototype = it
} }
return RhinoScriptEngine.eval(jsStr, scope) return RhinoScriptEngine.eval(jsStr, scope, coroutineContext)
} }
fun put(key: String, value: String): String { fun put(key: String, value: String): String {
@@ -476,7 +481,7 @@ class AnalyzeUrl(
sourceRegex: String? = null, sourceRegex: String? = null,
useWebView: Boolean = true, useWebView: Boolean = true,
): StrResponse { ): StrResponse {
return runBlocking { return runBlocking(coroutineContext) {
getStrResponseAwait(jsStr, sourceRegex, useWebView) getStrResponseAwait(jsStr, sourceRegex, useWebView)
} }
} }
@@ -527,7 +532,7 @@ class AnalyzeUrl(
} }
fun getResponse(): Response { fun getResponse(): Response {
return runBlocking { return runBlocking(coroutineContext) {
getResponseAwait() getResponseAwait()
} }
} }
@@ -553,7 +558,7 @@ class AnalyzeUrl(
} }
fun getByteArray(): ByteArray { fun getByteArray(): ByteArray {
return runBlocking { return runBlocking(coroutineContext) {
getByteArrayAwait() getByteArrayAwait()
} }
} }
@@ -569,7 +574,7 @@ class AnalyzeUrl(
} }
fun getInputStream(): InputStream { fun getInputStream(): InputStream {
return runBlocking { return runBlocking(coroutineContext) {
getInputStreamAwait() getInputStreamAwait()
} }
} }
@@ -63,12 +63,14 @@ object BookChapterList {
var nextUrl = chapterData.second[0] var nextUrl = chapterData.second[0]
while (nextUrl.isNotEmpty() && !nextUrlList.contains(nextUrl)) { while (nextUrl.isNotEmpty() && !nextUrlList.contains(nextUrl)) {
nextUrlList.add(nextUrl) nextUrlList.add(nextUrl)
val res = AnalyzeUrl( val analyzeUrl = AnalyzeUrl(
mUrl = nextUrl, mUrl = nextUrl,
source = bookSource, source = bookSource,
ruleData = book, ruleData = book,
headerMapF = bookSource.getHeaderMap() headerMapF = bookSource.getHeaderMap(),
).getStrResponseAwait() //控制并发访问 coroutineContext = coroutineContext
)
val res = analyzeUrl.getStrResponseAwait() //控制并发访问
res.body?.let { nextBody -> res.body?.let { nextBody ->
chapterData = analyzeChapterList( chapterData = analyzeChapterList(
book, nextUrl, nextUrl, book, nextUrl, nextUrl,
@@ -91,12 +93,14 @@ object BookChapterList {
emit(urlStr) emit(urlStr)
} }
}.mapAsync(AppConfig.threadCount) { urlStr -> }.mapAsync(AppConfig.threadCount) { urlStr ->
val res = AnalyzeUrl( val analyzeUrl = AnalyzeUrl(
mUrl = urlStr, mUrl = urlStr,
source = bookSource, source = bookSource,
ruleData = book, ruleData = book,
headerMapF = bookSource.getHeaderMap() headerMapF = bookSource.getHeaderMap(),
).getStrResponseAwait() //控制并发访问 coroutineContext = coroutineContext
)
val res = analyzeUrl.getStrResponseAwait() //控制并发访问
analyzeChapterList( analyzeChapterList(
book, urlStr, res.url, book, urlStr, res.url,
res.body!!, tocRule, listRule, bookSource, false res.body!!, tocRule, listRule, bookSource, false
@@ -86,12 +86,14 @@ object BookContent {
) break ) break
nextUrlList.add(nextUrl) nextUrlList.add(nextUrl)
coroutineContext.ensureActive() coroutineContext.ensureActive()
val res = AnalyzeUrl( val analyzeUrl = AnalyzeUrl(
mUrl = nextUrl, mUrl = nextUrl,
source = bookSource, source = bookSource,
ruleData = book, ruleData = book,
headerMapF = bookSource.getHeaderMap() headerMapF = bookSource.getHeaderMap(),
).getStrResponseAwait() //控制并发访问 coroutineContext = coroutineContext
)
val res = analyzeUrl.getStrResponseAwait() //控制并发访问
res.body?.let { nextBody -> res.body?.let { nextBody ->
contentData = analyzeContent( contentData = analyzeContent(
book, nextUrl, res.url, nextBody, contentRule, book, nextUrl, res.url, nextBody, contentRule,
@@ -112,12 +114,14 @@ object BookContent {
emit(urlStr) emit(urlStr)
} }
}.mapAsync(AppConfig.threadCount) { urlStr -> }.mapAsync(AppConfig.threadCount) { urlStr ->
val res = AnalyzeUrl( val analyzeUrl = AnalyzeUrl(
mUrl = urlStr, mUrl = urlStr,
source = bookSource, source = bookSource,
ruleData = book, ruleData = book,
headerMapF = bookSource.getHeaderMap() headerMapF = bookSource.getHeaderMap(),
).getStrResponseAwait() //控制并发访问 coroutineContext = coroutineContext
)
val res = analyzeUrl.getStrResponseAwait() //控制并发访问
analyzeContent( analyzeContent(
book, urlStr, res.url, res.body!!, contentRule, book, urlStr, res.url, res.body!!, contentRule,
bookChapter, bookSource, mNextChapterUrl, bookChapter, bookSource, mNextChapterUrl,
@@ -61,6 +61,7 @@ object WebBook {
headerMapF = bookSource.getHeaderMap(true), headerMapF = bookSource.getHeaderMap(true),
source = bookSource, source = bookSource,
ruleData = ruleData, ruleData = ruleData,
coroutineContext = coroutineContext
) )
var res = analyzeUrl.getStrResponseAwait() var res = analyzeUrl.getStrResponseAwait()
//检测书源是否已登录 //检测书源是否已登录
@@ -107,7 +108,8 @@ object WebBook {
baseUrl = bookSource.bookSourceUrl, baseUrl = bookSource.bookSourceUrl,
source = bookSource, source = bookSource,
ruleData = ruleData, ruleData = ruleData,
headerMapF = bookSource.getHeaderMap(true) headerMapF = bookSource.getHeaderMap(true),
coroutineContext = coroutineContext
) )
var res = analyzeUrl.getStrResponseAwait() var res = analyzeUrl.getStrResponseAwait()
//检测书源是否已登录 //检测书源是否已登录
@@ -164,7 +166,8 @@ object WebBook {
baseUrl = bookSource.bookSourceUrl, baseUrl = bookSource.bookSourceUrl,
source = bookSource, source = bookSource,
ruleData = book, ruleData = book,
headerMapF = bookSource.getHeaderMap(true) headerMapF = bookSource.getHeaderMap(true),
coroutineContext = coroutineContext
) )
var res = analyzeUrl.getStrResponseAwait() var res = analyzeUrl.getStrResponseAwait()
//检测书源是否已登录 //检测书源是否已登录
@@ -244,7 +247,8 @@ object WebBook {
baseUrl = book.bookUrl, baseUrl = book.bookUrl,
source = bookSource, source = bookSource,
ruleData = book, ruleData = book,
headerMapF = bookSource.getHeaderMap(true) headerMapF = bookSource.getHeaderMap(true),
coroutineContext = coroutineContext
) )
var res = analyzeUrl.getStrResponseAwait() var res = analyzeUrl.getStrResponseAwait()
//检测书源是否已登录 //检测书源是否已登录
@@ -317,7 +321,8 @@ object WebBook {
source = bookSource, source = bookSource,
ruleData = book, ruleData = book,
chapter = bookChapter, chapter = bookChapter,
headerMapF = bookSource.getHeaderMap(true) headerMapF = bookSource.getHeaderMap(true),
coroutineContext = coroutineContext
) )
var res = analyzeUrl.getStrResponseAwait( var res = analyzeUrl.getStrResponseAwait(
jsStr = bookSource.getContentRule().webJs, jsStr = bookSource.getContentRule().webJs,
@@ -90,10 +90,9 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
scope: Scriptable, scope: Scriptable,
coroutineContext: CoroutineContext? coroutineContext: CoroutineContext?
): Any? { ): Any? {
val cx = Context.enter() val cx = Context.enter() as RhinoContext
if (cx is RhinoContext) { val previousCoroutineContext = cx.coroutineContext
cx.coroutineContext = coroutineContext cx.coroutineContext = coroutineContext
}
val ret: Any? val ret: Any?
try { try {
var filename = this["javax.script.filename"] as? String var filename = this["javax.script.filename"] as? String
@@ -112,6 +111,7 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
} catch (var14: IOException) { } catch (var14: IOException) {
throw ScriptException(var14) throw ScriptException(var14)
} finally { } finally {
cx.coroutineContext = previousCoroutineContext
Context.exit() Context.exit()
} }
return unwrapReturnValue(ret) return unwrapReturnValue(ret)
@@ -355,6 +355,7 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
args: Array<Any> args: Array<Any>
): Any? { ): Any? {
try { try {
(cx as RhinoContext).ensureActive()
return super.doTopCall(callable, cx, scope, thisObj, args) return super.doTopCall(callable, cx, scope, thisObj, args)
} catch (e: RhinoInterruptError) { } catch (e: RhinoInterruptError) {
throw e.cause throw e.cause