#17 Show specific error if video is deleted
This commit is contained in:
parent
e8afb551ee
commit
b1e44dce43
22 changed files with 5945 additions and 65 deletions
|
|
@ -4,10 +4,11 @@ sealed class ProcessState {
|
||||||
|
|
||||||
data class Processing(val videoInPending: VideoInPending) : ProcessState()
|
data class Processing(val videoInPending: VideoInPending) : ProcessState()
|
||||||
data class Processed(val videoDownloaded: VideoDownloaded) : ProcessState()
|
data class Processed(val videoDownloaded: VideoDownloaded) : ProcessState()
|
||||||
object NetworkError : ProcessState()
|
data object NetworkError : ProcessState()
|
||||||
object ParsingError : ProcessState()
|
data object ParsingError : ProcessState()
|
||||||
object CaptchaError : ProcessState()
|
data object VideoDeletedError : ProcessState()
|
||||||
object UnknownError : ProcessState()
|
data object CaptchaError : ProcessState()
|
||||||
object StorageError : ProcessState()
|
data object UnknownError : ProcessState()
|
||||||
object Finished: ProcessState()
|
data object StorageError : ProcessState()
|
||||||
|
data object Finished: ProcessState()
|
||||||
}
|
}
|
||||||
|
|
@ -7,8 +7,10 @@ import org.fnives.tiktokdownloader.Logger
|
||||||
import org.fnives.tiktokdownloader.data.model.VideoInPending
|
import org.fnives.tiktokdownloader.data.model.VideoInPending
|
||||||
import org.fnives.tiktokdownloader.data.model.VideoInSavingIntoFile
|
import org.fnives.tiktokdownloader.data.model.VideoInSavingIntoFile
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredException
|
import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredException
|
||||||
|
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.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
|
||||||
|
|
@ -21,36 +23,42 @@ class TikTokDownloadRemoteSource(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@Throws(ParsingException::class, NetworkException::class, CaptchaRequiredException::class)
|
@Throws(ParsingException::class, NetworkException::class, CaptchaRequiredException::class)
|
||||||
suspend fun getVideo(videoInPending: VideoInPending): VideoInSavingIntoFile = withContext(Dispatchers.IO) {
|
suspend fun getVideo(videoInPending: VideoInPending): VideoInSavingIntoFile =
|
||||||
cookieStore.clear()
|
withContext(Dispatchers.IO) {
|
||||||
wrapIntoProperException {
|
cookieStore.clear()
|
||||||
delay(delayBeforeRequest) // added just so captcha trigger may not happen
|
wrapIntoProperException {
|
||||||
val actualUrl = service.getContentActualUrlAndCookie(videoInPending.url)
|
|
||||||
val videoUrl: VideoFileUrl
|
|
||||||
if (actualUrl.url != null) {
|
|
||||||
Logger.logMessage("actualUrl found = ${actualUrl.url}")
|
|
||||||
delay(delayBeforeRequest) // added just so captcha trigger may not happen
|
delay(delayBeforeRequest) // added just so captcha trigger may not happen
|
||||||
|
val actualUrl = service.getContentActualUrlAndCookie(videoInPending.url)
|
||||||
|
val videoUrl: VideoFileUrl
|
||||||
|
if (actualUrl.url != null) {
|
||||||
|
Logger.logMessage("actualUrl found = ${actualUrl.url}")
|
||||||
|
delay(delayBeforeRequest) // added just so captcha trigger may not happen
|
||||||
|
|
||||||
videoUrl = service.getVideoUrl(actualUrl.url)
|
videoUrl = service.getVideoUrl(actualUrl.url)
|
||||||
} else {
|
} else {
|
||||||
Logger.logMessage("actualUrl not found. Attempting to parse videoUrl")
|
Logger.logMessage("actualUrl not found. Attempting to parse videoUrl")
|
||||||
|
|
||||||
videoUrl = videoFileUrlConverter.convertSafely(actualUrl.fullResponse)
|
videoUrl = videoFileUrlConverter.convertSafely(actualUrl.fullResponse)
|
||||||
|
}
|
||||||
|
Logger.logMessage("videoFileUrl found = ${videoUrl.videoFileUrl}")
|
||||||
|
delay(delayBeforeRequest) // added just so captcha trigger may not happen
|
||||||
|
val response = service.getVideo(videoUrl.videoFileUrl)
|
||||||
|
|
||||||
|
VideoInSavingIntoFile(
|
||||||
|
id = videoInPending.id,
|
||||||
|
url = videoInPending.url,
|
||||||
|
contentType = response.mediaType?.let {
|
||||||
|
VideoInSavingIntoFile.ContentType(
|
||||||
|
it.type,
|
||||||
|
it.subtype
|
||||||
|
)
|
||||||
|
},
|
||||||
|
byteStream = response.videoInputStream
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Logger.logMessage("videoFileUrl found = ${videoUrl.videoFileUrl}")
|
|
||||||
delay(delayBeforeRequest) // added just so captcha trigger may not happen
|
|
||||||
val response = service.getVideo(videoUrl.videoFileUrl)
|
|
||||||
|
|
||||||
VideoInSavingIntoFile(
|
|
||||||
id = videoInPending.id,
|
|
||||||
url = videoInPending.url,
|
|
||||||
contentType = response.mediaType?.let { VideoInSavingIntoFile.ContentType(it.type, it.subtype) },
|
|
||||||
byteStream = response.videoInputStream
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(ParsingException::class, NetworkException::class)
|
@Throws(ParsingException::class, NetworkException::class, VideoDeletedException::class)
|
||||||
private suspend fun <T> wrapIntoProperException(request: suspend () -> T): T =
|
private suspend fun <T> wrapIntoProperException(request: suspend () -> T): T =
|
||||||
try {
|
try {
|
||||||
request()
|
request()
|
||||||
|
|
@ -58,7 +66,12 @@ class TikTokDownloadRemoteSource(
|
||||||
throw parsingException
|
throw parsingException
|
||||||
} catch (captchaRequiredException: CaptchaRequiredException) {
|
} catch (captchaRequiredException: CaptchaRequiredException) {
|
||||||
throw captchaRequiredException
|
throw captchaRequiredException
|
||||||
|
} catch (videoDeletedException: VideoDeletedException) {
|
||||||
|
throw videoDeletedException
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
throw NetworkException(cause = throwable)
|
throw NetworkException(
|
||||||
|
cause = throwable,
|
||||||
|
html = (throwable as? HtmlException)?.html.orEmpty()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
package org.fnives.tiktokdownloader.data.network.exceptions
|
package org.fnives.tiktokdownloader.data.network.exceptions
|
||||||
|
|
||||||
class CaptchaRequiredException(message: String? = null, cause: Throwable? = null) : Throwable(message, cause)
|
class CaptchaRequiredException(
|
||||||
|
message: String? = null, cause: Throwable? = null,
|
||||||
|
override val html: String,
|
||||||
|
) : Throwable(message, cause), HtmlException
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package org.fnives.tiktokdownloader.data.network.exceptions
|
||||||
|
|
||||||
|
interface HtmlException {
|
||||||
|
val html: String
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
package org.fnives.tiktokdownloader.data.network.exceptions
|
package org.fnives.tiktokdownloader.data.network.exceptions
|
||||||
|
|
||||||
class NetworkException(message: String? = null, cause: Throwable? = null) : Throwable(message, cause)
|
class NetworkException(
|
||||||
|
message: String? = null, cause: Throwable? = null,
|
||||||
|
override val html: String,
|
||||||
|
) : Throwable(message, cause), HtmlException
|
||||||
|
|
@ -2,4 +2,7 @@ package org.fnives.tiktokdownloader.data.network.exceptions
|
||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class ParsingException(message: String? = null, cause: Throwable? = null) : IOException(message, cause)
|
class ParsingException(
|
||||||
|
message: String? = null, cause: Throwable? = null,
|
||||||
|
override val html: String,
|
||||||
|
) : IOException(message, cause), HtmlException
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
package org.fnives.tiktokdownloader.data.network.exceptions
|
||||||
|
|
||||||
|
class VideoDeletedException(override val html: String) : Throwable(),
|
||||||
|
HtmlException
|
||||||
|
|
@ -3,6 +3,7 @@ package org.fnives.tiktokdownloader.data.network.parsing
|
||||||
import okhttp3.ResponseBody
|
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.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
|
||||||
|
|
@ -12,7 +13,10 @@ import retrofit2.Converter
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
class TikTokWebPageConverterFactory(private val throwIfIsCaptchaResponse: ThrowIfIsCaptchaResponse) : Converter.Factory() {
|
class TikTokWebPageConverterFactory(
|
||||||
|
private val throwIfIsCaptchaResponse: ThrowIfIsCaptchaResponse,
|
||||||
|
private val throwIfVideoIsDeletedResponse: ThrowIfVideoIsDeletedResponse
|
||||||
|
) : Converter.Factory() {
|
||||||
|
|
||||||
override fun responseBodyConverter(
|
override fun responseBodyConverter(
|
||||||
type: Type,
|
type: Type,
|
||||||
|
|
@ -20,8 +24,12 @@ class TikTokWebPageConverterFactory(private val throwIfIsCaptchaResponse: ThrowI
|
||||||
retrofit: Retrofit
|
retrofit: Retrofit
|
||||||
): Converter<ResponseBody, *>? =
|
): Converter<ResponseBody, *>? =
|
||||||
when (type) {
|
when (type) {
|
||||||
ActualVideoPageUrl::class.java -> ActualVideoPageUrlConverter(throwIfIsCaptchaResponse)
|
ActualVideoPageUrl::class.java -> ActualVideoPageUrlConverter(
|
||||||
VideoFileUrl::class.java -> VideoFileUrlConverter(throwIfIsCaptchaResponse)
|
throwIfIsCaptchaResponse,
|
||||||
|
throwIfVideoIsDeletedResponse
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ import org.fnives.tiktokdownloader.data.network.parsing.response.ActualVideoPage
|
||||||
import kotlin.jvm.Throws
|
import kotlin.jvm.Throws
|
||||||
|
|
||||||
class ActualVideoPageUrlConverter(
|
class ActualVideoPageUrlConverter(
|
||||||
private val throwIfIsCaptchaResponse: ThrowIfIsCaptchaResponse
|
private val throwIfIsCaptchaResponse: ThrowIfIsCaptchaResponse,
|
||||||
|
private val throwIfVideoIsDeletedResponse: ThrowIfVideoIsDeletedResponse
|
||||||
) : ParsingExceptionThrowingConverter<ActualVideoPageUrl>() {
|
) : ParsingExceptionThrowingConverter<ActualVideoPageUrl>() {
|
||||||
|
|
||||||
@Throws(IndexOutOfBoundsException::class, CaptchaRequiredException::class)
|
@Throws(IndexOutOfBoundsException::class, CaptchaRequiredException::class)
|
||||||
|
|
@ -15,6 +16,7 @@ class ActualVideoPageUrlConverter(
|
||||||
return try {
|
return try {
|
||||||
val actualVideoPageUrl = responseBodyAsString
|
val actualVideoPageUrl = responseBodyAsString
|
||||||
.also(throwIfIsCaptchaResponse::invoke)
|
.also(throwIfIsCaptchaResponse::invoke)
|
||||||
|
.also(throwIfVideoIsDeletedResponse::invoke)
|
||||||
.split("rel=\"canonical\" href=\"")[1]
|
.split("rel=\"canonical\" href=\"")[1]
|
||||||
.split("\"")[0]
|
.split("\"")[0]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,25 +2,32 @@ 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.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 retrofit2.Converter
|
import retrofit2.Converter
|
||||||
|
|
||||||
abstract class ParsingExceptionThrowingConverter<T> : Converter<ResponseBody, T> {
|
abstract class ParsingExceptionThrowingConverter<T> : Converter<ResponseBody, T> {
|
||||||
|
|
||||||
@Throws(ParsingException::class, CaptchaRequiredException::class)
|
@Throws(ParsingException::class, CaptchaRequiredException::class, VideoDeletedException::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)
|
@Throws(ParsingException::class, CaptchaRequiredException::class, VideoDeletedException::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) {
|
||||||
|
throw videoDeletedException
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
throw ParsingException(cause = throwable)
|
throw ParsingException(
|
||||||
|
cause = throwable,
|
||||||
|
html = (throwable as? HtmlException)?.html.orEmpty()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,15 @@
|
||||||
package org.fnives.tiktokdownloader.data.network.parsing.converter
|
package org.fnives.tiktokdownloader.data.network.parsing.converter
|
||||||
|
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredException
|
import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredException
|
||||||
import kotlin.jvm.Throws
|
|
||||||
|
|
||||||
class ThrowIfIsCaptchaResponse {
|
class ThrowIfIsCaptchaResponse {
|
||||||
|
|
||||||
@Throws(CaptchaRequiredException::class)
|
@Throws(CaptchaRequiredException::class)
|
||||||
fun invoke(html: String) {
|
fun invoke(html: String) {
|
||||||
if (html.isEmpty()) {
|
if (html.isEmpty()) {
|
||||||
throw CaptchaRequiredException("Empty body")
|
throw CaptchaRequiredException("Empty body", html = html)
|
||||||
} else if (html.contains("captcha.js")) {
|
} else if (html.contains("captcha.js")) {
|
||||||
throw CaptchaRequiredException("Contains Captcha keyword")
|
throw CaptchaRequiredException("Contains Captcha keyword", html = html)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package org.fnives.tiktokdownloader.data.network.parsing.converter
|
||||||
|
|
||||||
|
import org.fnives.tiktokdownloader.data.network.exceptions.VideoDeletedException
|
||||||
|
|
||||||
|
class ThrowIfVideoIsDeletedResponse {
|
||||||
|
|
||||||
|
@Throws(VideoDeletedException::class)
|
||||||
|
fun invoke(html: String) {
|
||||||
|
if (html.contains("\"statusMsg\":\"status_deleted\"")) {
|
||||||
|
throw VideoDeletedException(html = html)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,8 @@ import org.fnives.tiktokdownloader.data.network.exceptions.ParsingException
|
||||||
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,
|
||||||
) : ParsingExceptionThrowingConverter<VideoFileUrl>() {
|
) : ParsingExceptionThrowingConverter<VideoFileUrl>() {
|
||||||
|
|
||||||
@Throws(IllegalArgumentException::class, IndexOutOfBoundsException::class, CaptchaRequiredException::class)
|
@Throws(IllegalArgumentException::class, IndexOutOfBoundsException::class, CaptchaRequiredException::class)
|
||||||
|
|
@ -23,6 +24,7 @@ class VideoFileUrlConverter(
|
||||||
@Throws(IllegalArgumentException::class, IndexOutOfBoundsException::class, CaptchaRequiredException::class)
|
@Throws(IllegalArgumentException::class, IndexOutOfBoundsException::class, CaptchaRequiredException::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)
|
||||||
val url = tryToParseDownloadLink(html).also { Logger.logMessage("parsed download link = $it") }
|
val url = tryToParseDownloadLink(html).also { Logger.logMessage("parsed download link = $it") }
|
||||||
?: tryToParseVideoSrc(html).also { Logger.logMessage("parsed video src = $it") }
|
?: tryToParseVideoSrc(html).also { Logger.logMessage("parsed video src = $it") }
|
||||||
?: throw IllegalArgumentException("Couldn't parse url from HTML: $html")
|
?: throw IllegalArgumentException("Couldn't parse url from HTML: $html")
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package org.fnives.tiktokdownloader.data.usecase
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
@ -30,6 +31,7 @@ import org.fnives.tiktokdownloader.data.network.TikTokDownloadRemoteSource
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredException
|
import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredException
|
||||||
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
|
||||||
|
|
||||||
@OptIn(FlowPreview::class)
|
@OptIn(FlowPreview::class)
|
||||||
class VideoDownloadingProcessorUseCase(
|
class VideoDownloadingProcessorUseCase(
|
||||||
|
|
@ -42,6 +44,8 @@ class VideoDownloadingProcessorUseCase(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val fetch = MutableStateFlow(ProcessingState.RUNNING)
|
private val fetch = MutableStateFlow(ProcessingState.RUNNING)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
private val _processState by lazy {
|
private val _processState by lazy {
|
||||||
combineIntoPair(fetch, videoInPendingLocalSource.observeFirstPendingVideo())
|
combineIntoPair(fetch, videoInPendingLocalSource.observeFirstPendingVideo())
|
||||||
.filter { it.first == ProcessingState.RUNNING }
|
.filter { it.first == ProcessingState.RUNNING }
|
||||||
|
|
@ -84,13 +88,17 @@ class VideoDownloadingProcessorUseCase(
|
||||||
videoInPendingLocalSource.removeVideoFromQueue(videoInPending)
|
videoInPendingLocalSource.removeVideoFromQueue(videoInPending)
|
||||||
alreadyDownloaded
|
alreadyDownloaded
|
||||||
}
|
}
|
||||||
|
|
||||||
captchaTimeoutLocalSource.isInCaptchaTimeout() -> {
|
captchaTimeoutLocalSource.isInCaptchaTimeout() -> {
|
||||||
throw CaptchaRequiredException("In Captcha Timeout!")
|
throw CaptchaRequiredException("In Captcha Timeout!", html = "")
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
videoInProgressLocalSource.markVideoAsInProgress(videoInPending)
|
videoInProgressLocalSource.markVideoAsInProgress(videoInPending)
|
||||||
val videoInSavingIntoFile: VideoInSavingIntoFile = tikTokDownloadRemoteSource.getVideo(videoInPending)
|
val videoInSavingIntoFile: VideoInSavingIntoFile =
|
||||||
val videoDownloaded: VideoDownloaded = videoDownloadedLocalSource.saveVideo(videoInSavingIntoFile)
|
tikTokDownloadRemoteSource.getVideo(videoInPending)
|
||||||
|
val videoDownloaded: VideoDownloaded =
|
||||||
|
videoDownloadedLocalSource.saveVideo(videoInSavingIntoFile)
|
||||||
videoInPendingLocalSource.removeVideoFromQueue(videoInPending)
|
videoInPendingLocalSource.removeVideoFromQueue(videoInPending)
|
||||||
|
|
||||||
videoDownloaded
|
videoDownloaded
|
||||||
|
|
@ -102,6 +110,8 @@ class VideoDownloadingProcessorUseCase(
|
||||||
ProcessState.NetworkError
|
ProcessState.NetworkError
|
||||||
} catch (parsingException: ParsingException) {
|
} catch (parsingException: ParsingException) {
|
||||||
ProcessState.ParsingError
|
ProcessState.ParsingError
|
||||||
|
} catch (videoDeletedException: VideoDeletedException) {
|
||||||
|
ProcessState.VideoDeletedError
|
||||||
} catch (storageException: StorageException) {
|
} catch (storageException: StorageException) {
|
||||||
ProcessState.StorageError
|
ProcessState.StorageError
|
||||||
} catch (captchaRequiredException: CaptchaRequiredException) {
|
} catch (captchaRequiredException: CaptchaRequiredException) {
|
||||||
|
|
@ -124,10 +134,12 @@ class VideoDownloadingProcessorUseCase(
|
||||||
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.CaptchaError -> true
|
ProcessState.CaptchaError -> true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import org.fnives.tiktokdownloader.data.network.TikTokDownloadRemoteSource
|
||||||
import org.fnives.tiktokdownloader.data.network.TikTokRetrofitService
|
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.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
|
||||||
|
|
@ -18,8 +19,11 @@ class NetworkModule(private val delayBeforeRequest: Long) {
|
||||||
private val throwIfIsCaptchaResponse: ThrowIfIsCaptchaResponse
|
private val throwIfIsCaptchaResponse: ThrowIfIsCaptchaResponse
|
||||||
get() = ThrowIfIsCaptchaResponse()
|
get() = ThrowIfIsCaptchaResponse()
|
||||||
|
|
||||||
|
private val throwIfVideoIsDeletedResponse: ThrowIfVideoIsDeletedResponse
|
||||||
|
get() = ThrowIfVideoIsDeletedResponse()
|
||||||
|
|
||||||
private val tikTokConverterFactory: Converter.Factory
|
private val tikTokConverterFactory: Converter.Factory
|
||||||
get() = TikTokWebPageConverterFactory(throwIfIsCaptchaResponse)
|
get() = TikTokWebPageConverterFactory(throwIfIsCaptchaResponse, throwIfVideoIsDeletedResponse)
|
||||||
|
|
||||||
private val cookieSavingInterceptor: CookieSavingInterceptor by lazy { CookieSavingInterceptor() }
|
private val cookieSavingInterceptor: CookieSavingInterceptor by lazy { CookieSavingInterceptor() }
|
||||||
|
|
||||||
|
|
@ -48,5 +52,5 @@ 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))
|
get() = TikTokDownloadRemoteSource(delayBeforeRequest, tikTokRetrofitService, cookieStore, VideoFileUrlConverter(throwIfIsCaptchaResponse, throwIfVideoIsDeletedResponse))
|
||||||
}
|
}
|
||||||
|
|
@ -104,6 +104,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
MainViewModel.ErrorMessage.STORAGE -> R.string.storage_error
|
MainViewModel.ErrorMessage.STORAGE -> R.string.storage_error
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun animateFabClicked(downloadFab: FloatingActionButton) {
|
private fun animateFabClicked(downloadFab: FloatingActionButton) {
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ class MainViewModel(
|
||||||
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
|
||||||
}
|
}
|
||||||
val refreshActionVisibility = when (it) {
|
val refreshActionVisibility = when (it) {
|
||||||
is ProcessState.Processing,
|
is ProcessState.Processing,
|
||||||
|
|
@ -54,6 +55,7 @@ class MainViewModel(
|
||||||
ProcessState.ParsingError,
|
ProcessState.ParsingError,
|
||||||
ProcessState.StorageError,
|
ProcessState.StorageError,
|
||||||
ProcessState.UnknownError,
|
ProcessState.UnknownError,
|
||||||
|
ProcessState.VideoDeletedError,
|
||||||
ProcessState.CaptchaError -> true
|
ProcessState.CaptchaError -> true
|
||||||
}
|
}
|
||||||
_errorMessage.postValue(errorMessage?.let(::Event))
|
_errorMessage.postValue(errorMessage?.let(::Event))
|
||||||
|
|
@ -71,7 +73,7 @@ class MainViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ErrorMessage {
|
enum class ErrorMessage {
|
||||||
NETWORK, PARSING, STORAGE, CAPTCHA, UNKNOWN
|
NETWORK, PARSING, STORAGE, CAPTCHA, UNKNOWN, DELETED
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Screen {
|
enum class Screen {
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,8 @@ class QueueServiceViewModel(
|
||||||
ProcessState.Finished -> NotificationState.Finish
|
ProcessState.Finished -> NotificationState.Finish
|
||||||
ProcessState.CaptchaError ->
|
ProcessState.CaptchaError ->
|
||||||
NotificationState.Error(R.string.captcha_error)
|
NotificationState.Error(R.string.captcha_error)
|
||||||
|
|
||||||
|
ProcessState.VideoDeletedError -> NotificationState.Error(R.string.video_deleted_error)
|
||||||
}
|
}
|
||||||
_notificationState.postValue(value)
|
_notificationState.postValue(value)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
<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="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>
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
fun GIVEN_one_pending_video_AND_network_error_WHEN_observing_THEN_error_is_emited() = runBlocking {
|
fun GIVEN_one_pending_video_AND_network_error_WHEN_observing_THEN_error_is_emited() = runBlocking {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw NetworkException() }
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw NetworkException(html = "") }
|
||||||
val expected = ProcessState.NetworkError
|
val expected = ProcessState.NetworkError
|
||||||
val expectedList = listOf(ProcessState.Processing(videoInPending), expected)
|
val expectedList = listOf(ProcessState.Processing(videoInPending), expected)
|
||||||
|
|
||||||
|
|
@ -137,7 +137,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
fun GIVEN_one_pending_video_AND_parsing_error_WHEN_observing_THEN_parsingError_is_emited() = runBlocking {
|
fun GIVEN_one_pending_video_AND_parsing_error_WHEN_observing_THEN_parsingError_is_emited() = runBlocking {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw ParsingException() }
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw ParsingException(html = "") }
|
||||||
val expected = ProcessState.ParsingError
|
val expected = ProcessState.ParsingError
|
||||||
val expectedList = listOf(ProcessState.Processing(videoInPending), expected)
|
val expectedList = listOf(ProcessState.Processing(videoInPending), expected)
|
||||||
|
|
||||||
|
|
@ -167,7 +167,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
var specificException = true
|
var specificException = true
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then {
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then {
|
||||||
throw if (specificException) NetworkException().also { specificException = false } else Throwable()
|
throw if (specificException) NetworkException(html = "").also { specificException = false } else Throwable()
|
||||||
}
|
}
|
||||||
val inProgressItem = ProcessState.Processing(videoInPending)
|
val inProgressItem = ProcessState.Processing(videoInPending)
|
||||||
val expectedList = listOf(inProgressItem, ProcessState.NetworkError, inProgressItem, ProcessState.UnknownError)
|
val expectedList = listOf(inProgressItem, ProcessState.NetworkError, inProgressItem, ProcessState.UnknownError)
|
||||||
|
|
@ -186,7 +186,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
var specificException = true
|
var specificException = true
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then {
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then {
|
||||||
throw if (specificException) ParsingException().also { specificException = false } else Throwable()
|
throw if (specificException) ParsingException(html = "").also { specificException = false } else Throwable()
|
||||||
}
|
}
|
||||||
val inProgressItem = ProcessState.Processing(videoInPending)
|
val inProgressItem = ProcessState.Processing(videoInPending)
|
||||||
val expectedList = listOf(inProgressItem, ProcessState.ParsingError, inProgressItem, ProcessState.UnknownError)
|
val expectedList = listOf(inProgressItem, ProcessState.ParsingError, inProgressItem, ProcessState.UnknownError)
|
||||||
|
|
@ -205,7 +205,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
var specificException = true
|
var specificException = true
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then {
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then {
|
||||||
throw if (specificException) Throwable().also { specificException = false } else NetworkException()
|
throw if (specificException) Throwable().also { specificException = false } else NetworkException(html = "")
|
||||||
}
|
}
|
||||||
val inProgressItem = ProcessState.Processing(videoInPending)
|
val inProgressItem = ProcessState.Processing(videoInPending)
|
||||||
val expectedList = listOf(inProgressItem, ProcessState.UnknownError, inProgressItem, ProcessState.NetworkError)
|
val expectedList = listOf(inProgressItem, ProcessState.UnknownError, inProgressItem, ProcessState.NetworkError)
|
||||||
|
|
@ -229,7 +229,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
sut.fetchVideoInState()
|
sut.fetchVideoInState()
|
||||||
specificException = false
|
specificException = false
|
||||||
|
|
||||||
NetworkException()
|
NetworkException(html = "")
|
||||||
} else {
|
} else {
|
||||||
Throwable()
|
Throwable()
|
||||||
}
|
}
|
||||||
|
|
@ -250,7 +250,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then {
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then {
|
||||||
throw NetworkException()
|
throw NetworkException(html = "")
|
||||||
}
|
}
|
||||||
val inProgressItem = ProcessState.Processing(videoInPending)
|
val inProgressItem = ProcessState.Processing(videoInPending)
|
||||||
val expectedList = listOf(inProgressItem, ProcessState.NetworkError)
|
val expectedList = listOf(inProgressItem, ProcessState.NetworkError)
|
||||||
|
|
@ -433,7 +433,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
ProcessState.CaptchaError
|
ProcessState.CaptchaError
|
||||||
)
|
)
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then {
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then {
|
||||||
throw CaptchaRequiredException()
|
throw CaptchaRequiredException(html = "")
|
||||||
}
|
}
|
||||||
|
|
||||||
val resultList = async(testDispatcher) { sut.processState.take(2).toList() }
|
val resultList = async(testDispatcher) { sut.processState.take(2).toList() }
|
||||||
|
|
@ -459,7 +459,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
fun GIVEN_one_pending_video_AND_not_advancing_enough_WHILE_observing_WHEN_fetching_THEN_nothing_is_called() = runBlocking<Unit> {
|
fun GIVEN_one_pending_video_AND_not_advancing_enough_WHILE_observing_WHEN_fetching_THEN_nothing_is_called() = runBlocking<Unit> {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw NetworkException() }
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw NetworkException(html = "") }
|
||||||
|
|
||||||
val resultList = async(testDispatcher) { sut.processState.take(2).toList() }
|
val resultList = async(testDispatcher) { sut.processState.take(2).toList() }
|
||||||
testDispatcher.advanceTimeBy(199)
|
testDispatcher.advanceTimeBy(199)
|
||||||
|
|
@ -473,7 +473,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
fun GIVEN_one_pending_video_AND_but_advancing_enough_WHILE_observing_WHEN_fetching_THEN_nothing_is_called() = runBlocking<Unit> {
|
fun GIVEN_one_pending_video_AND_but_advancing_enough_WHILE_observing_WHEN_fetching_THEN_nothing_is_called() = runBlocking<Unit> {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw NetworkException() }
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw NetworkException(html = "") }
|
||||||
|
|
||||||
val resultList = async(testDispatcher) { sut.processState.take(2).toList() }
|
val resultList = async(testDispatcher) { sut.processState.take(2).toList() }
|
||||||
testDispatcher.advanceTimeBy(201)
|
testDispatcher.advanceTimeBy(201)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import kotlinx.coroutines.runBlocking
|
||||||
import org.apache.commons.io.FileUtils
|
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.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
|
||||||
|
|
@ -37,7 +38,7 @@ class TikTokDownloadRemoteSourceUpToDateTest {
|
||||||
actualFile.delete()
|
actualFile.delete()
|
||||||
actualFile.createNewFile()
|
actualFile.createNewFile()
|
||||||
actualFile.deleteOnExit()
|
actualFile.deleteOnExit()
|
||||||
val expectedFileOptions = EXPECTED_FILE_PATHS.map{getResourceFile(it)}
|
val expectedFileOptions = EXPECTED_FILE_PATHS.map { getResourceFile(it) }
|
||||||
actualFile.writeText("")
|
actualFile.writeText("")
|
||||||
|
|
||||||
runBlocking { sut.getVideo(parameter).byteStream }.use { inputStream ->
|
runBlocking { sut.getVideo(parameter).byteStream }.use { inputStream ->
|
||||||
|
|
@ -45,15 +46,30 @@ class TikTokDownloadRemoteSourceUpToDateTest {
|
||||||
inputStream.copyTo(outputStream)
|
inputStream.copyTo(outputStream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val doesAnyIsTheSameFile = expectedFileOptions.any { expectedFile->
|
val doesAnyIsTheSameFile = expectedFileOptions.any { expectedFile ->
|
||||||
FileUtils.contentEquals(expectedFile, actualFile)
|
FileUtils.contentEquals(expectedFile, actualFile)
|
||||||
}
|
}
|
||||||
Assertions.assertTrue(doesAnyIsTheSameFile, "The Downloaded file Is Not Matching the expected")
|
Assertions.assertTrue(
|
||||||
|
doesAnyIsTheSameFile,
|
||||||
|
"The Downloaded file Is Not Matching the expected"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Timeout(value = 120)
|
||||||
|
@Test
|
||||||
|
fun GIVEN_deleted_WHEN_downloading_THEN_proper_exception_is_thrown() {
|
||||||
|
val parameter = VideoInPending("123", DELETED_VIDEO_URL)
|
||||||
|
Assertions.assertThrows(VideoDeletedException::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 = listOf("video/expected_option_1.mp4","video/expected_option_2.mp4")
|
private val EXPECTED_FILE_PATHS =
|
||||||
|
listOf("video/expected_option_1.mp4", "video/expected_option_2.mp4")
|
||||||
private const val SUBJECT_VIDEO_URL = "https://vm.tiktok.com/ZSQG7SMf/"
|
private const val SUBJECT_VIDEO_URL = "https://vm.tiktok.com/ZSQG7SMf/"
|
||||||
|
private const val PRIVATE_VIDEO_URL = "https://vm.tiktok.com/ZNdM4EjTQ/"
|
||||||
|
private const val DELETED_VIDEO_URL = "https://vm.tiktok.com/ZNdMVM4WG/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
5779
app/src/test/resources/response/deleted_video.html
Normal file
5779
app/src/test/resources/response/deleted_video.html
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue