BarInsta/app/src/main/java/awais/instagrabber/webservices/MediaRepository.kt

197 lines
6.4 KiB
Kotlin

package awais.instagrabber.webservices
import awais.instagrabber.models.enums.MediaItemType
import awais.instagrabber.repositories.MediaService
import awais.instagrabber.repositories.requests.Clip
import awais.instagrabber.repositories.requests.UploadFinishOptions
import awais.instagrabber.repositories.responses.Media
import awais.instagrabber.repositories.responses.User
import awais.instagrabber.utils.DateUtils
import awais.instagrabber.utils.Utils
import awais.instagrabber.utils.retryContextString
import awais.instagrabber.webservices.RetrofitFactory.retrofit
import org.json.JSONObject
class MediaRepository(private val service: MediaService) {
suspend fun fetch(
mediaId: Long,
): Media? {
val response = service.fetch(mediaId)
return if (response.items.isNullOrEmpty()) {
null
} else response.items[0]
}
suspend fun like(
csrfToken: String,
userId: Long,
deviceUuid: String,
mediaId: String,
): Boolean = action(csrfToken, userId, deviceUuid, mediaId, "like", null)
suspend fun unlike(
csrfToken: String,
userId: Long,
deviceUuid: String,
mediaId: String,
): Boolean = action(csrfToken, userId, deviceUuid, mediaId, "unlike", null)
suspend fun save(
csrfToken: String,
userId: Long,
deviceUuid: String,
mediaId: String, collection: String?,
): Boolean = action(csrfToken, userId, deviceUuid, mediaId, "save", collection)
suspend fun unsave(
csrfToken: String,
userId: Long,
deviceUuid: String,
mediaId: String,
): Boolean = action(csrfToken, userId, deviceUuid, mediaId, "unsave", null)
private suspend fun action(
csrfToken: String,
userId: Long,
deviceUuid: String,
mediaId: String,
action: String,
collection: String?,
): Boolean {
val form: MutableMap<String, Any> = mutableMapOf(
"media_id" to mediaId,
"_csrftoken" to csrfToken,
"_uid" to userId,
"_uuid" to deviceUuid,
)
// form.put("radio_type", "wifi-none");
if (action == "save" && !collection.isNullOrBlank()) {
form["added_collection_ids"] = "[$collection]"
}
// there also exists "removed_collection_ids" which can be used with "save" and "unsave"
val signedForm = Utils.sign(form)
val response = service.action(action, mediaId, signedForm)
val jsonObject = JSONObject(response)
val status = jsonObject.optString("status")
return status == "ok"
}
suspend fun editCaption(
csrfToken: String,
userId: Long,
deviceUuid: String,
postId: String,
newCaption: String,
): Boolean {
val form = mapOf(
"_csrftoken" to csrfToken,
"_uid" to userId,
"_uuid" to deviceUuid,
"igtv_feed_preview" to "false",
"media_id" to postId,
"caption_text" to newCaption,
)
val signedForm = Utils.sign(form)
val response = service.editCaption(postId, signedForm)
val jsonObject = JSONObject(response)
val status = jsonObject.optString("status")
return status == "ok"
}
suspend fun fetchLikes(
mediaId: String,
isComment: Boolean,
): List<User> {
val response = service.fetchLikes(mediaId, if (isComment) "comment_likers" else "likers")
return response.users
}
suspend fun translate(
id: String,
type: String, // 1 caption 2 comment 3 bio
): String? {
val form = mapOf(
"id" to id,
"type" to type,
)
val response = service.translate(form)
val jsonObject = JSONObject(response)
if (!jsonObject.has("translation") || jsonObject.isNull("translation")) {
return null
}
return jsonObject.getString("translation")
}
suspend fun uploadFinish(
csrfToken: String,
userId: Long,
deviceUuid: String,
options: UploadFinishOptions,
): String {
if (options.videoOptions != null) {
val videoOptions = options.videoOptions
if (videoOptions.clips.isEmpty()) {
videoOptions.clips = listOf(Clip(videoOptions.length, options.sourceType))
}
}
val timezoneOffset = DateUtils.timezoneOffset.toString()
val form = mutableMapOf<String, Any>(
"timezone_offset" to timezoneOffset,
"_csrftoken" to csrfToken,
"source_type" to options.sourceType,
"_uid" to userId.toString(),
"_uuid" to deviceUuid,
"upload_id" to options.uploadId,
)
if (options.videoOptions != null) {
form.putAll(options.videoOptions.map)
}
val queryMap = if (options.videoOptions != null) mapOf("video" to "1") else emptyMap()
val signedForm = Utils.sign(form)
return service.uploadFinish(retryContextString, queryMap, signedForm)
}
suspend fun delete(
csrfToken: String,
userId: Long,
deviceUuid: String,
postId: String,
type: MediaItemType,
): String? {
if (!DELETABLE_ITEMS_TYPES.contains(type)) return null
val form = mapOf(
"_csrftoken" to csrfToken,
"_uid" to userId,
"_uuid" to deviceUuid,
"igtv_feed_preview" to "false",
"media_id" to postId,
)
val signedForm = Utils.sign(form)
val mediaType: String = when (type) {
MediaItemType.MEDIA_TYPE_IMAGE -> "PHOTO"
MediaItemType.MEDIA_TYPE_VIDEO -> "VIDEO"
MediaItemType.MEDIA_TYPE_SLIDER -> "CAROUSEL"
else -> return null
}
return service.delete(postId, mediaType, signedForm)
}
companion object {
@Volatile
private var INSTANCE: MediaRepository? = null
private val DELETABLE_ITEMS_TYPES = listOf(
MediaItemType.MEDIA_TYPE_IMAGE,
MediaItemType.MEDIA_TYPE_VIDEO,
MediaItemType.MEDIA_TYPE_SLIDER
)
fun getInstance(): MediaRepository {
return INSTANCE ?: synchronized(this) {
val service = retrofit.create(MediaService::class.java)
MediaRepository(service).also { INSTANCE = it }
}
}
}
}