#17 Show specific error if video is private
This commit is contained in:
parent
b1e44dce43
commit
f9bc9679dc
16 changed files with 136 additions and 29 deletions
|
|
@ -7,6 +7,7 @@ sealed class ProcessState {
|
||||||
data object NetworkError : ProcessState()
|
data object NetworkError : ProcessState()
|
||||||
data object ParsingError : ProcessState()
|
data object ParsingError : ProcessState()
|
||||||
data object VideoDeletedError : ProcessState()
|
data object VideoDeletedError : ProcessState()
|
||||||
|
data object VideoPrivateError : ProcessState()
|
||||||
data object CaptchaError : ProcessState()
|
data object CaptchaError : ProcessState()
|
||||||
data object UnknownError : ProcessState()
|
data object UnknownError : ProcessState()
|
||||||
data object StorageError : ProcessState()
|
data object StorageError : ProcessState()
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import org.fnives.tiktokdownloader.data.network.exceptions.HtmlException
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.NetworkException
|
import org.fnives.tiktokdownloader.data.network.exceptions.NetworkException
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.ParsingException
|
import org.fnives.tiktokdownloader.data.network.exceptions.ParsingException
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.VideoDeletedException
|
import org.fnives.tiktokdownloader.data.network.exceptions.VideoDeletedException
|
||||||
|
import org.fnives.tiktokdownloader.data.network.exceptions.VideoPrivateException
|
||||||
import org.fnives.tiktokdownloader.data.network.parsing.converter.VideoFileUrlConverter
|
import org.fnives.tiktokdownloader.data.network.parsing.converter.VideoFileUrlConverter
|
||||||
import org.fnives.tiktokdownloader.data.network.parsing.response.VideoFileUrl
|
import org.fnives.tiktokdownloader.data.network.parsing.response.VideoFileUrl
|
||||||
import org.fnives.tiktokdownloader.data.network.session.CookieStore
|
import org.fnives.tiktokdownloader.data.network.session.CookieStore
|
||||||
|
|
@ -68,6 +69,8 @@ class TikTokDownloadRemoteSource(
|
||||||
throw captchaRequiredException
|
throw captchaRequiredException
|
||||||
} catch (videoDeletedException: VideoDeletedException) {
|
} catch (videoDeletedException: VideoDeletedException) {
|
||||||
throw videoDeletedException
|
throw videoDeletedException
|
||||||
|
} catch (videoPrivateException: VideoPrivateException) {
|
||||||
|
throw videoPrivateException
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
throw NetworkException(
|
throw NetworkException(
|
||||||
cause = throwable,
|
cause = throwable,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
package org.fnives.tiktokdownloader.data.network.exceptions
|
||||||
|
|
||||||
|
class VideoPrivateException(override val html: String) : Throwable(),
|
||||||
|
HtmlException
|
||||||
|
|
@ -4,6 +4,7 @@ import okhttp3.ResponseBody
|
||||||
import org.fnives.tiktokdownloader.data.network.parsing.converter.ActualVideoPageUrlConverter
|
import org.fnives.tiktokdownloader.data.network.parsing.converter.ActualVideoPageUrlConverter
|
||||||
import org.fnives.tiktokdownloader.data.network.parsing.converter.ThrowIfIsCaptchaResponse
|
import org.fnives.tiktokdownloader.data.network.parsing.converter.ThrowIfIsCaptchaResponse
|
||||||
import org.fnives.tiktokdownloader.data.network.parsing.converter.ThrowIfVideoIsDeletedResponse
|
import org.fnives.tiktokdownloader.data.network.parsing.converter.ThrowIfVideoIsDeletedResponse
|
||||||
|
import org.fnives.tiktokdownloader.data.network.parsing.converter.ThrowIfVideoIsPrivateResponse
|
||||||
import org.fnives.tiktokdownloader.data.network.parsing.converter.VideoFileUrlConverter
|
import org.fnives.tiktokdownloader.data.network.parsing.converter.VideoFileUrlConverter
|
||||||
import org.fnives.tiktokdownloader.data.network.parsing.converter.VideoResponseConverter
|
import org.fnives.tiktokdownloader.data.network.parsing.converter.VideoResponseConverter
|
||||||
import org.fnives.tiktokdownloader.data.network.parsing.response.ActualVideoPageUrl
|
import org.fnives.tiktokdownloader.data.network.parsing.response.ActualVideoPageUrl
|
||||||
|
|
@ -15,7 +16,8 @@ import java.lang.reflect.Type
|
||||||
|
|
||||||
class TikTokWebPageConverterFactory(
|
class TikTokWebPageConverterFactory(
|
||||||
private val throwIfIsCaptchaResponse: ThrowIfIsCaptchaResponse,
|
private val throwIfIsCaptchaResponse: ThrowIfIsCaptchaResponse,
|
||||||
private val throwIfVideoIsDeletedResponse: ThrowIfVideoIsDeletedResponse
|
private val throwIfVideoIsDeletedResponse: ThrowIfVideoIsDeletedResponse,
|
||||||
|
private val throwIfVideoIsPrivateResponse: ThrowIfVideoIsPrivateResponse,
|
||||||
) : Converter.Factory() {
|
) : Converter.Factory() {
|
||||||
|
|
||||||
override fun responseBodyConverter(
|
override fun responseBodyConverter(
|
||||||
|
|
@ -26,10 +28,16 @@ class TikTokWebPageConverterFactory(
|
||||||
when (type) {
|
when (type) {
|
||||||
ActualVideoPageUrl::class.java -> ActualVideoPageUrlConverter(
|
ActualVideoPageUrl::class.java -> ActualVideoPageUrlConverter(
|
||||||
throwIfIsCaptchaResponse,
|
throwIfIsCaptchaResponse,
|
||||||
throwIfVideoIsDeletedResponse
|
throwIfVideoIsDeletedResponse,
|
||||||
|
throwIfVideoIsPrivateResponse,
|
||||||
|
)
|
||||||
|
|
||||||
|
VideoFileUrl::class.java -> VideoFileUrlConverter(
|
||||||
|
throwIfIsCaptchaResponse,
|
||||||
|
throwIfVideoIsDeletedResponse,
|
||||||
|
throwIfVideoIsPrivateResponse
|
||||||
)
|
)
|
||||||
|
|
||||||
VideoFileUrl::class.java -> VideoFileUrlConverter(throwIfIsCaptchaResponse, throwIfVideoIsDeletedResponse)
|
|
||||||
VideoResponse::class.java -> VideoResponseConverter()
|
VideoResponse::class.java -> VideoResponseConverter()
|
||||||
else -> super.responseBodyConverter(type, annotations, retrofit)
|
else -> super.responseBodyConverter(type, annotations, retrofit)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,26 +2,33 @@ package org.fnives.tiktokdownloader.data.network.parsing.converter
|
||||||
|
|
||||||
import okhttp3.ResponseBody
|
import okhttp3.ResponseBody
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredException
|
import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredException
|
||||||
|
import org.fnives.tiktokdownloader.data.network.exceptions.VideoDeletedException
|
||||||
|
import org.fnives.tiktokdownloader.data.network.exceptions.VideoPrivateException
|
||||||
import org.fnives.tiktokdownloader.data.network.parsing.response.ActualVideoPageUrl
|
import org.fnives.tiktokdownloader.data.network.parsing.response.ActualVideoPageUrl
|
||||||
import kotlin.jvm.Throws
|
|
||||||
|
|
||||||
class ActualVideoPageUrlConverter(
|
class ActualVideoPageUrlConverter(
|
||||||
private val throwIfIsCaptchaResponse: ThrowIfIsCaptchaResponse,
|
private val throwIfIsCaptchaResponse: ThrowIfIsCaptchaResponse,
|
||||||
private val throwIfVideoIsDeletedResponse: ThrowIfVideoIsDeletedResponse
|
private val throwIfVideoIsDeletedResponse: ThrowIfVideoIsDeletedResponse,
|
||||||
|
private val throwIfVideoIsPrivateResponse: ThrowIfVideoIsPrivateResponse
|
||||||
) : ParsingExceptionThrowingConverter<ActualVideoPageUrl>() {
|
) : ParsingExceptionThrowingConverter<ActualVideoPageUrl>() {
|
||||||
|
|
||||||
@Throws(IndexOutOfBoundsException::class, CaptchaRequiredException::class)
|
@Throws(
|
||||||
|
IndexOutOfBoundsException::class, CaptchaRequiredException::class,
|
||||||
|
VideoDeletedException::class,
|
||||||
|
VideoPrivateException::class,
|
||||||
|
)
|
||||||
override fun convertSafely(responseBody: ResponseBody): ActualVideoPageUrl {
|
override fun convertSafely(responseBody: ResponseBody): ActualVideoPageUrl {
|
||||||
val responseBodyAsString =responseBody.string()
|
val responseBodyAsString = responseBody.string()
|
||||||
return try {
|
return try {
|
||||||
val actualVideoPageUrl = responseBodyAsString
|
val actualVideoPageUrl = responseBodyAsString
|
||||||
.also(throwIfIsCaptchaResponse::invoke)
|
.also(throwIfIsCaptchaResponse::invoke)
|
||||||
.also(throwIfVideoIsDeletedResponse::invoke)
|
.also(throwIfVideoIsDeletedResponse::invoke)
|
||||||
|
.also(throwIfVideoIsPrivateResponse::invoke)
|
||||||
.split("rel=\"canonical\" href=\"")[1]
|
.split("rel=\"canonical\" href=\"")[1]
|
||||||
.split("\"")[0]
|
.split("\"")[0]
|
||||||
|
|
||||||
ActualVideoPageUrl(actualVideoPageUrl, responseBodyAsString)
|
ActualVideoPageUrl(actualVideoPageUrl, responseBodyAsString)
|
||||||
} catch(_: Throwable) {
|
} catch (_: Throwable) {
|
||||||
ActualVideoPageUrl(null, responseBodyAsString)
|
ActualVideoPageUrl(null, responseBodyAsString)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,24 +5,37 @@ import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredExcept
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.HtmlException
|
import org.fnives.tiktokdownloader.data.network.exceptions.HtmlException
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.ParsingException
|
import org.fnives.tiktokdownloader.data.network.exceptions.ParsingException
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.VideoDeletedException
|
import org.fnives.tiktokdownloader.data.network.exceptions.VideoDeletedException
|
||||||
|
import org.fnives.tiktokdownloader.data.network.exceptions.VideoPrivateException
|
||||||
import retrofit2.Converter
|
import retrofit2.Converter
|
||||||
|
|
||||||
abstract class ParsingExceptionThrowingConverter<T> : Converter<ResponseBody, T> {
|
abstract class ParsingExceptionThrowingConverter<T> : Converter<ResponseBody, T> {
|
||||||
|
|
||||||
@Throws(ParsingException::class, CaptchaRequiredException::class, VideoDeletedException::class)
|
@Throws(
|
||||||
|
ParsingException::class,
|
||||||
|
CaptchaRequiredException::class,
|
||||||
|
VideoDeletedException::class,
|
||||||
|
VideoPrivateException::class
|
||||||
|
)
|
||||||
final override fun convert(value: ResponseBody): T? =
|
final override fun convert(value: ResponseBody): T? =
|
||||||
doActionSafely {
|
doActionSafely {
|
||||||
convertSafely(value)
|
convertSafely(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(ParsingException::class, CaptchaRequiredException::class, VideoDeletedException::class)
|
@Throws(
|
||||||
|
ParsingException::class,
|
||||||
|
CaptchaRequiredException::class,
|
||||||
|
VideoDeletedException::class,
|
||||||
|
VideoPrivateException::class
|
||||||
|
)
|
||||||
fun doActionSafely(action: () -> T): T {
|
fun doActionSafely(action: () -> T): T {
|
||||||
try {
|
try {
|
||||||
return action()
|
return action()
|
||||||
} catch (captchaRequiredException: CaptchaRequiredException) {
|
} catch (captchaRequiredException: CaptchaRequiredException) {
|
||||||
throw captchaRequiredException
|
throw captchaRequiredException
|
||||||
} catch(videoDeletedException: VideoDeletedException) {
|
} catch (videoDeletedException: VideoDeletedException) {
|
||||||
throw videoDeletedException
|
throw videoDeletedException
|
||||||
|
} catch (videoPrivateException: VideoPrivateException) {
|
||||||
|
throw videoPrivateException
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
throw ParsingException(
|
throw ParsingException(
|
||||||
cause = throwable,
|
cause = throwable,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ class ThrowIfVideoIsDeletedResponse {
|
||||||
|
|
||||||
@Throws(VideoDeletedException::class)
|
@Throws(VideoDeletedException::class)
|
||||||
fun invoke(html: String) {
|
fun invoke(html: String) {
|
||||||
if (html.contains("\"statusMsg\":\"status_deleted\"")) {
|
if (html.contains("\"statusMsg\":\"status_deleted")) {
|
||||||
throw VideoDeletedException(html = html)
|
throw VideoDeletedException(html = html)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package org.fnives.tiktokdownloader.data.network.parsing.converter
|
||||||
|
|
||||||
|
import org.fnives.tiktokdownloader.data.network.exceptions.VideoPrivateException
|
||||||
|
|
||||||
|
class ThrowIfVideoIsPrivateResponse {
|
||||||
|
|
||||||
|
@Throws(VideoPrivateException::class)
|
||||||
|
fun invoke(html: String) {
|
||||||
|
if (html.contains("\"statusMsg\":\"status_friend_see")) {
|
||||||
|
throw VideoPrivateException(html = html)
|
||||||
|
} else if (html.contains("\"statusMsg\":\"author_secret")) {
|
||||||
|
throw VideoPrivateException(html = html)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,14 +4,21 @@ import okhttp3.ResponseBody
|
||||||
import org.fnives.tiktokdownloader.Logger
|
import org.fnives.tiktokdownloader.Logger
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredException
|
import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredException
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.ParsingException
|
import org.fnives.tiktokdownloader.data.network.exceptions.ParsingException
|
||||||
|
import org.fnives.tiktokdownloader.data.network.exceptions.VideoDeletedException
|
||||||
|
import org.fnives.tiktokdownloader.data.network.exceptions.VideoPrivateException
|
||||||
import org.fnives.tiktokdownloader.data.network.parsing.response.VideoFileUrl
|
import org.fnives.tiktokdownloader.data.network.parsing.response.VideoFileUrl
|
||||||
|
|
||||||
class VideoFileUrlConverter(
|
class VideoFileUrlConverter(
|
||||||
private val throwIfIsCaptchaResponse: ThrowIfIsCaptchaResponse,
|
private val throwIfIsCaptchaResponse: ThrowIfIsCaptchaResponse,
|
||||||
private val throwIfVideoIsDeletedResponse: ThrowIfVideoIsDeletedResponse,
|
private val throwIfVideoIsDeletedResponse: ThrowIfVideoIsDeletedResponse,
|
||||||
|
private val throwIfVideoIsPrivateResponse: ThrowIfVideoIsPrivateResponse
|
||||||
) : ParsingExceptionThrowingConverter<VideoFileUrl>() {
|
) : ParsingExceptionThrowingConverter<VideoFileUrl>() {
|
||||||
|
|
||||||
@Throws(IllegalArgumentException::class, IndexOutOfBoundsException::class, CaptchaRequiredException::class)
|
@Throws(
|
||||||
|
IllegalArgumentException::class,
|
||||||
|
IndexOutOfBoundsException::class,
|
||||||
|
CaptchaRequiredException::class
|
||||||
|
)
|
||||||
override fun convertSafely(responseBody: ResponseBody): VideoFileUrl {
|
override fun convertSafely(responseBody: ResponseBody): VideoFileUrl {
|
||||||
return convert(responseBody.string())
|
return convert(responseBody.string())
|
||||||
}
|
}
|
||||||
|
|
@ -21,13 +28,21 @@ class VideoFileUrlConverter(
|
||||||
return doActionSafely { convert(responseBody) }
|
return doActionSafely { convert(responseBody) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IllegalArgumentException::class, IndexOutOfBoundsException::class, CaptchaRequiredException::class)
|
@Throws(
|
||||||
|
IllegalArgumentException::class,
|
||||||
|
IndexOutOfBoundsException::class,
|
||||||
|
CaptchaRequiredException::class,
|
||||||
|
VideoDeletedException::class,
|
||||||
|
VideoPrivateException::class,
|
||||||
|
)
|
||||||
private fun convert(responseBody: String): VideoFileUrl {
|
private fun convert(responseBody: String): VideoFileUrl {
|
||||||
val html = responseBody.also(throwIfIsCaptchaResponse::invoke)
|
val html = responseBody.also(throwIfIsCaptchaResponse::invoke)
|
||||||
.also(throwIfVideoIsDeletedResponse::invoke)
|
.also(throwIfVideoIsDeletedResponse::invoke)
|
||||||
val url = tryToParseDownloadLink(html).also { Logger.logMessage("parsed download link = $it") }
|
.also(throwIfVideoIsPrivateResponse::invoke)
|
||||||
?: tryToParseVideoSrc(html).also { Logger.logMessage("parsed video src = $it") }
|
val url =
|
||||||
?: throw IllegalArgumentException("Couldn't parse url from HTML: $html")
|
tryToParseDownloadLink(html).also { Logger.logMessage("parsed download link = $it") }
|
||||||
|
?: tryToParseVideoSrc(html).also { Logger.logMessage("parsed video src = $it") }
|
||||||
|
?: throw IllegalArgumentException("Couldn't parse url from HTML: $html")
|
||||||
|
|
||||||
return VideoFileUrl(url)
|
return VideoFileUrl(url)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredExcept
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.NetworkException
|
import org.fnives.tiktokdownloader.data.network.exceptions.NetworkException
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.ParsingException
|
import org.fnives.tiktokdownloader.data.network.exceptions.ParsingException
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.VideoDeletedException
|
import org.fnives.tiktokdownloader.data.network.exceptions.VideoDeletedException
|
||||||
|
import org.fnives.tiktokdownloader.data.network.exceptions.VideoPrivateException
|
||||||
|
|
||||||
@OptIn(FlowPreview::class)
|
@OptIn(FlowPreview::class)
|
||||||
class VideoDownloadingProcessorUseCase(
|
class VideoDownloadingProcessorUseCase(
|
||||||
|
|
@ -112,6 +113,8 @@ class VideoDownloadingProcessorUseCase(
|
||||||
ProcessState.ParsingError
|
ProcessState.ParsingError
|
||||||
} catch (videoDeletedException: VideoDeletedException) {
|
} catch (videoDeletedException: VideoDeletedException) {
|
||||||
ProcessState.VideoDeletedError
|
ProcessState.VideoDeletedError
|
||||||
|
} catch (videoPrivateException: VideoPrivateException) {
|
||||||
|
ProcessState.VideoPrivateError
|
||||||
} catch (storageException: StorageException) {
|
} catch (storageException: StorageException) {
|
||||||
ProcessState.StorageError
|
ProcessState.StorageError
|
||||||
} catch (captchaRequiredException: CaptchaRequiredException) {
|
} catch (captchaRequiredException: CaptchaRequiredException) {
|
||||||
|
|
@ -140,6 +143,7 @@ class VideoDownloadingProcessorUseCase(
|
||||||
ProcessState.StorageError,
|
ProcessState.StorageError,
|
||||||
ProcessState.UnknownError,
|
ProcessState.UnknownError,
|
||||||
ProcessState.VideoDeletedError,
|
ProcessState.VideoDeletedError,
|
||||||
|
ProcessState.VideoPrivateError,
|
||||||
ProcessState.CaptchaError -> true
|
ProcessState.CaptchaError -> true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import org.fnives.tiktokdownloader.data.network.TikTokRetrofitService
|
||||||
import org.fnives.tiktokdownloader.data.network.parsing.TikTokWebPageConverterFactory
|
import org.fnives.tiktokdownloader.data.network.parsing.TikTokWebPageConverterFactory
|
||||||
import org.fnives.tiktokdownloader.data.network.parsing.converter.ThrowIfIsCaptchaResponse
|
import org.fnives.tiktokdownloader.data.network.parsing.converter.ThrowIfIsCaptchaResponse
|
||||||
import org.fnives.tiktokdownloader.data.network.parsing.converter.ThrowIfVideoIsDeletedResponse
|
import org.fnives.tiktokdownloader.data.network.parsing.converter.ThrowIfVideoIsDeletedResponse
|
||||||
|
import org.fnives.tiktokdownloader.data.network.parsing.converter.ThrowIfVideoIsPrivateResponse
|
||||||
import org.fnives.tiktokdownloader.data.network.parsing.converter.VideoFileUrlConverter
|
import org.fnives.tiktokdownloader.data.network.parsing.converter.VideoFileUrlConverter
|
||||||
import org.fnives.tiktokdownloader.data.network.session.CookieSavingInterceptor
|
import org.fnives.tiktokdownloader.data.network.session.CookieSavingInterceptor
|
||||||
import org.fnives.tiktokdownloader.data.network.session.CookieStore
|
import org.fnives.tiktokdownloader.data.network.session.CookieStore
|
||||||
|
|
@ -22,8 +23,15 @@ class NetworkModule(private val delayBeforeRequest: Long) {
|
||||||
private val throwIfVideoIsDeletedResponse: ThrowIfVideoIsDeletedResponse
|
private val throwIfVideoIsDeletedResponse: ThrowIfVideoIsDeletedResponse
|
||||||
get() = ThrowIfVideoIsDeletedResponse()
|
get() = ThrowIfVideoIsDeletedResponse()
|
||||||
|
|
||||||
|
private val throwIfVideoIsPrivateResponse: ThrowIfVideoIsPrivateResponse
|
||||||
|
get() = ThrowIfVideoIsPrivateResponse()
|
||||||
|
|
||||||
private val tikTokConverterFactory: Converter.Factory
|
private val tikTokConverterFactory: Converter.Factory
|
||||||
get() = TikTokWebPageConverterFactory(throwIfIsCaptchaResponse, throwIfVideoIsDeletedResponse)
|
get() = TikTokWebPageConverterFactory(
|
||||||
|
throwIfIsCaptchaResponse,
|
||||||
|
throwIfVideoIsDeletedResponse,
|
||||||
|
throwIfVideoIsPrivateResponse
|
||||||
|
)
|
||||||
|
|
||||||
private val cookieSavingInterceptor: CookieSavingInterceptor by lazy { CookieSavingInterceptor() }
|
private val cookieSavingInterceptor: CookieSavingInterceptor by lazy { CookieSavingInterceptor() }
|
||||||
|
|
||||||
|
|
@ -34,7 +42,9 @@ class NetworkModule(private val delayBeforeRequest: Long) {
|
||||||
.addInterceptor(cookieSavingInterceptor)
|
.addInterceptor(cookieSavingInterceptor)
|
||||||
.let {
|
.let {
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
it.addInterceptor(HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY })
|
it.addInterceptor(HttpLoggingInterceptor().apply {
|
||||||
|
level = HttpLoggingInterceptor.Level.BODY
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
it
|
it
|
||||||
}
|
}
|
||||||
|
|
@ -52,5 +62,14 @@ class NetworkModule(private val delayBeforeRequest: Long) {
|
||||||
get() = retrofit.create(TikTokRetrofitService::class.java)
|
get() = retrofit.create(TikTokRetrofitService::class.java)
|
||||||
|
|
||||||
val tikTokDownloadRemoteSource: TikTokDownloadRemoteSource
|
val tikTokDownloadRemoteSource: TikTokDownloadRemoteSource
|
||||||
get() = TikTokDownloadRemoteSource(delayBeforeRequest, tikTokRetrofitService, cookieStore, VideoFileUrlConverter(throwIfIsCaptchaResponse, throwIfVideoIsDeletedResponse))
|
get() = TikTokDownloadRemoteSource(
|
||||||
|
delayBeforeRequest,
|
||||||
|
tikTokRetrofitService,
|
||||||
|
cookieStore,
|
||||||
|
VideoFileUrlConverter(
|
||||||
|
throwIfIsCaptchaResponse,
|
||||||
|
throwIfVideoIsDeletedResponse,
|
||||||
|
throwIfVideoIsPrivateResponse
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -105,6 +105,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
MainViewModel.ErrorMessage.CAPTCHA -> R.string.captcha_error
|
MainViewModel.ErrorMessage.CAPTCHA -> R.string.captcha_error
|
||||||
MainViewModel.ErrorMessage.UNKNOWN -> R.string.unexpected_error
|
MainViewModel.ErrorMessage.UNKNOWN -> R.string.unexpected_error
|
||||||
MainViewModel.ErrorMessage.DELETED -> R.string.video_deleted_error
|
MainViewModel.ErrorMessage.DELETED -> R.string.video_deleted_error
|
||||||
|
MainViewModel.ErrorMessage.PRIVATE -> R.string.video_private_error
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun animateFabClicked(downloadFab: FloatingActionButton) {
|
private fun animateFabClicked(downloadFab: FloatingActionButton) {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.fnives.tiktokdownloader.data.model.ProcessState
|
import org.fnives.tiktokdownloader.data.model.ProcessState
|
||||||
import org.fnives.tiktokdownloader.data.usecase.AddVideoToQueueUseCase
|
import org.fnives.tiktokdownloader.data.usecase.AddVideoToQueueUseCase
|
||||||
|
|
@ -22,13 +21,15 @@ class MainViewModel(
|
||||||
|
|
||||||
private val _refreshActionVisibility = MutableLiveData<Boolean>()
|
private val _refreshActionVisibility = MutableLiveData<Boolean>()
|
||||||
private val currentScreen = MutableLiveData<Screen>()
|
private val currentScreen = MutableLiveData<Screen>()
|
||||||
val refreshActionVisibility: LiveData<Boolean?> = combineNullable(_refreshActionVisibility, currentScreen) { refreshVisibility, screen ->
|
val refreshActionVisibility: LiveData<Boolean?> =
|
||||||
refreshVisibility == true && screen == Screen.QUEUE
|
combineNullable(_refreshActionVisibility, currentScreen) { refreshVisibility, screen ->
|
||||||
}
|
refreshVisibility == true && screen == Screen.QUEUE
|
||||||
|
}
|
||||||
private val _errorMessage = MutableLiveData<Event<ErrorMessage>>()
|
private val _errorMessage = MutableLiveData<Event<ErrorMessage>>()
|
||||||
val errorMessage: LiveData<Event<ErrorMessage>?> = combineNullable(_errorMessage, currentScreen) { event, screen ->
|
val errorMessage: LiveData<Event<ErrorMessage>?> =
|
||||||
event?.takeIf { screen == Screen.QUEUE }
|
combineNullable(_errorMessage, currentScreen) { event, screen ->
|
||||||
}
|
event?.takeIf { screen == Screen.QUEUE }
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
savedStateHandle.get<String>(INTENT_EXTRA_URL)?.let(addVideoToQueueUseCase::invoke)
|
savedStateHandle.get<String>(INTENT_EXTRA_URL)?.let(addVideoToQueueUseCase::invoke)
|
||||||
|
|
@ -40,22 +41,26 @@ class MainViewModel(
|
||||||
is ProcessState.Processing,
|
is ProcessState.Processing,
|
||||||
is ProcessState.Processed,
|
is ProcessState.Processed,
|
||||||
ProcessState.Finished -> null
|
ProcessState.Finished -> null
|
||||||
|
|
||||||
ProcessState.NetworkError -> ErrorMessage.NETWORK
|
ProcessState.NetworkError -> ErrorMessage.NETWORK
|
||||||
ProcessState.ParsingError -> ErrorMessage.PARSING
|
ProcessState.ParsingError -> ErrorMessage.PARSING
|
||||||
ProcessState.StorageError -> ErrorMessage.STORAGE
|
ProcessState.StorageError -> ErrorMessage.STORAGE
|
||||||
ProcessState.CaptchaError -> ErrorMessage.CAPTCHA
|
ProcessState.CaptchaError -> ErrorMessage.CAPTCHA
|
||||||
ProcessState.UnknownError -> ErrorMessage.UNKNOWN
|
ProcessState.UnknownError -> ErrorMessage.UNKNOWN
|
||||||
ProcessState.VideoDeletedError -> ErrorMessage.DELETED
|
ProcessState.VideoDeletedError -> ErrorMessage.DELETED
|
||||||
|
ProcessState.VideoPrivateError -> ErrorMessage.PRIVATE
|
||||||
}
|
}
|
||||||
val refreshActionVisibility = when (it) {
|
val refreshActionVisibility = when (it) {
|
||||||
is ProcessState.Processing,
|
is ProcessState.Processing,
|
||||||
is ProcessState.Processed,
|
is ProcessState.Processed,
|
||||||
ProcessState.Finished -> false
|
ProcessState.Finished -> false
|
||||||
|
|
||||||
ProcessState.NetworkError,
|
ProcessState.NetworkError,
|
||||||
ProcessState.ParsingError,
|
ProcessState.ParsingError,
|
||||||
ProcessState.StorageError,
|
ProcessState.StorageError,
|
||||||
ProcessState.UnknownError,
|
ProcessState.UnknownError,
|
||||||
ProcessState.VideoDeletedError,
|
ProcessState.VideoDeletedError,
|
||||||
|
ProcessState.VideoPrivateError,
|
||||||
ProcessState.CaptchaError -> true
|
ProcessState.CaptchaError -> true
|
||||||
}
|
}
|
||||||
_errorMessage.postValue(errorMessage?.let(::Event))
|
_errorMessage.postValue(errorMessage?.let(::Event))
|
||||||
|
|
@ -73,7 +78,7 @@ class MainViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ErrorMessage {
|
enum class ErrorMessage {
|
||||||
NETWORK, PARSING, STORAGE, CAPTCHA, UNKNOWN, DELETED
|
NETWORK, PARSING, STORAGE, CAPTCHA, UNKNOWN, DELETED, PRIVATE
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Screen {
|
enum class Screen {
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ class QueueServiceViewModel(
|
||||||
NotificationState.Error(R.string.captcha_error)
|
NotificationState.Error(R.string.captcha_error)
|
||||||
|
|
||||||
ProcessState.VideoDeletedError -> NotificationState.Error(R.string.video_deleted_error)
|
ProcessState.VideoDeletedError -> NotificationState.Error(R.string.video_deleted_error)
|
||||||
|
ProcessState.VideoPrivateError -> NotificationState.Error(R.string.video_private_error)
|
||||||
}
|
}
|
||||||
_notificationState.postValue(value)
|
_notificationState.postValue(value)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@
|
||||||
<string name="parsing_error">Parsing Error</string>
|
<string name="parsing_error">Parsing Error</string>
|
||||||
<string name="storage_error">Failed to Store Video</string>
|
<string name="storage_error">Failed to Store Video</string>
|
||||||
<string name="unexpected_error">Unexpected Error</string>
|
<string name="unexpected_error">Unexpected Error</string>
|
||||||
<string name="video_deleted_error">Video seems to be Deleted</string>
|
<string name="video_deleted_error">Video seems to be DELETED</string>
|
||||||
|
<string name="video_private_error">Video seems to be PRIVATE</string>
|
||||||
<string name="permission_request">Permission Needed</string>
|
<string name="permission_request">Permission Needed</string>
|
||||||
<string name="permission_rationale">External Storage permission is needed in order to save the video to your device</string>
|
<string name="permission_rationale">External Storage permission is needed in order to save the video to your device</string>
|
||||||
<string name="ok">OK</string>
|
<string name="ok">OK</string>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import org.apache.commons.io.FileUtils
|
||||||
import org.fnives.tiktokdownloader.data.model.VideoInPending
|
import org.fnives.tiktokdownloader.data.model.VideoInPending
|
||||||
import org.fnives.tiktokdownloader.data.network.TikTokDownloadRemoteSource
|
import org.fnives.tiktokdownloader.data.network.TikTokDownloadRemoteSource
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.VideoDeletedException
|
import org.fnives.tiktokdownloader.data.network.exceptions.VideoDeletedException
|
||||||
|
import org.fnives.tiktokdownloader.data.network.exceptions.VideoPrivateException
|
||||||
import org.fnives.tiktokdownloader.di.module.NetworkModule
|
import org.fnives.tiktokdownloader.di.module.NetworkModule
|
||||||
import org.fnives.tiktokdownloader.helper.getResourceFile
|
import org.fnives.tiktokdownloader.helper.getResourceFile
|
||||||
import org.junit.jupiter.api.Assertions
|
import org.junit.jupiter.api.Assertions
|
||||||
|
|
@ -64,6 +65,15 @@ class TikTokDownloadRemoteSourceUpToDateTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Timeout(value = 120)
|
||||||
|
@Test
|
||||||
|
fun GIVEN_private_WHEN_downloading_THEN_proper_exception_is_thrown() {
|
||||||
|
val parameter = VideoInPending("123", PRIVATE_VIDEO_URL)
|
||||||
|
Assertions.assertThrows(VideoPrivateException::class.java) {
|
||||||
|
runBlocking { sut.getVideo(parameter) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val ACTUAL_FILE_PATH = "actual.mp4"
|
private const val ACTUAL_FILE_PATH = "actual.mp4"
|
||||||
private val EXPECTED_FILE_PATHS =
|
private val EXPECTED_FILE_PATHS =
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue