All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.stormbit.tiktokapi.TikTokApi.kt Maven / Gradle / Ivy

There is a newer version: 1.0.2
Show newest version
package com.github.stormbit.tiktokapi

import com.google.gson.Gson
import com.google.gson.JsonObject
import net.dongliu.requests.Requests
import org.apache.http.NameValuePair
import org.apache.http.client.utils.URLEncodedUtils
import org.apache.http.message.BasicNameValuePair
import java.lang.Thread.sleep
import kotlin.random.Random

private const val BASE_URL = "https://m.tiktok.com/"

@Suppress("unused", "unused_variable", "MemberVisibilityCanBePrivate")
class TikTokApi(private val request_delay: Int = 0, executablePath: String, external_signer: String? = null) {
    private var signerUrl: String? = null
    private var userAgent: String = ""
    private var referrer: String? = null
    private var width = "1920"
    private var height = "1080"
    private val browserLanguage = ""
    private val browserPlatform = ""
    private val browserName = ""
    private val browserVersion = ""
    private val timezoneName = ""
    private lateinit var browser: Browser

    init {
        if (external_signer == null) {
            this.browser = Browser(executablePath = executablePath)
            this.userAgent = browser.userAgent
            this.referrer = browser.referrer
            this.width = browser.width
            this.height = browser.height
        }
    }

    fun close() {
        browser.close()
    }

    fun getTiktokById(id: String, params: HashMap): JsonObject {
        val (region, language, proxy, maxCount, did) = processParams(params)

        val query = listOf(
                BasicNameValuePair("itemId", id),
                BasicNameValuePair("language", language)
        )

        val apiUrl = "%sapi/item/detail/?%s&%s".format(BASE_URL, addNewParams(), URLEncodedUtils.format(query, "utf-8"))

        val prms = hashMapOf("url" to apiUrl)

        prms.putAll(params)

        return getData(prms)
    }

    fun getTikTokByUrl(url: String, params: HashMap): JsonObject {
        var postId = ""

        if (url.contains("@") && url.contains("/video/")) {
            postId = url.split("/video/")[1].split("?")[0]
        }

        return getTiktokById(postId, params)
    }

    fun getVideoURL(url: String, params: HashMap): String {
        val tiktokSchema = getTikTokByUrl(url, params)

        val download_url = tiktokSchema.get("itemInfo").asJsonObject.get("itemStruct").asJsonObject.get("video").asJsonObject.get("downloadAddr").asString

        return download_url
    }

    fun getBytes(params: HashMap): ByteArray {
        val (region, language, proxy, maxCount, did) = processParams(params)
        params["custom_did"] = did
        if (request_delay != 0) {
            sleep(request_delay.toLong())
        }

        val verifyFp: String
        val didd: String
        val signature: String
        val userAgent: String
        val referrer: String

        if (this.signerUrl == null) {
            val args = this.browser.signUrl(params)

            verifyFp = args[0]
            didd = args[1]
            signature = args[2]

            userAgent = this.browser.userAgent
            referrer = this.browser.referrer
        } else {
            val resp = externalSigner(params["url"].toString(), params.getOrDefault("custom_did", null).toString())
            verifyFp = resp[0]
            didd = resp[1]
            signature = resp[2]
            userAgent = resp[3]
            referrer = resp[4]
        }
        val query = listOf(
                BasicNameValuePair("verifyFp", verifyFp),
                BasicNameValuePair("_signature", signature)
        )

        val url = "%s&%s".format(params["url"], URLEncodedUtils.format(query, "utf-8"))
        val url2 = "https://v16-web.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/d40364f8143c495fbb718a3dbf2641f4/?a=1988&br=924&bt=462&cr=0&cs=0&dr=0&ds=2&er=&expire=1605476212&l=2020111515363101018907414801124BE0&lr=tiktok_m&mime_type=video_mp4&policy=2&qs=0&rc=andndHJ2eGdoeDMzNjczM0ApOWQ6ODQ8OTtmNzRmOjMzZWdvbHFyXjIwY2tfLS0tMTRzcy42Yi9eNTVgYDQtNTU2MTU6Yw%3D%3D&signature=9a38cc496936df261c22db24447352d6&tk=tt_webid_v2&vl=&vr=&verifyFp=verify_khgp4f49_V12d4mRX_MdCO_4Wzt_Ar0k_z4RCQC9pUDpX&_signature=_02B4Z6wo00f01cUV0hgAAIBBz-Rqd-BUVYHFBNaAAC7357"

        val headers = mapOf(
                "Accept" to "*/*",
                "Accept-Encoding" to "identity;q=1, *;q=0",
                "Accept-Language" to "en-US;en;q=0.9",
                "Cache-Control" to "no-cache",
                "Connection" to "keep-alive",
                "cookie" to "tt_webid_v2=$didd",
                "Host" to url.split("/")[2],
                "Pragma" to "no-cache",
                "Range" to "bytes=0-",
                "Referer" to referrer,
                "User-Agent" to userAgent,
        )

        val bytes = Requests.get(url)
                .headers(headers).send().readToBytes()

        return bytes
    }

    fun getVideoBytesByURL(url: String, params: HashMap = HashMap()): ByteArray {
        val (region, language, proxy, maxCount, did) = processParams(params)
        params["custom_did"] = did

        val downloadUrl = getVideoURL(url, params)

        val prms = hashMapOf(
                "url" to downloadUrl
        )

        prms.putAll(params)

        return getBytes(prms)
    }

    private fun getData(params: HashMap = HashMap()): JsonObject {
        val (region, language, proxy, maxCount, did) = processParams(params)
        params["custom_did"] = did
        if (request_delay != 0) {
            sleep(request_delay.toLong())
        }

        val verifyFp: String
        val didd: String
        val signature: String
        val userAgent: String
        val referrer: String

        if (this.signerUrl == null) {
            val args = this.browser.signUrl(params)

            verifyFp = args[0]
            didd = args[1]
            signature = args[2]

            userAgent = this.browser.userAgent
            referrer = this.browser.referrer
        } else {
            val resp = externalSigner(params["url"].toString(), params.getOrDefault("custom_did", null).toString())
            verifyFp = resp[0]
            didd = resp[1]
            signature = resp[2]
            userAgent = resp[3]
            referrer = resp[4]

        }
        val query = listOf(
                BasicNameValuePair("verifyFp", verifyFp),
                BasicNameValuePair("did", didd),
                BasicNameValuePair("_signature", signature)
        )

        val url = "%s&%s".format(params["url"], URLEncodedUtils.format(query, "utf-8"))

        val headers = mapOf(
                "authority" to "m.tiktok.com",
                "method" to "GET",
                "path" to url.split("tiktok.com")[1],
                "scheme" to "https",
                "accept" to "application/json, text/plain, */*",
                "accept-encoding" to "gzip, deflate, br",
                "accept-language" to "en-US,en;q=0.9",
                "referer" to referrer,
                "sec-fetch-dest" to "empty",
                "sec-fetch-mode" to "cors",
                "sec-fetch-site" to "same-site",
                "user-agent" to userAgent,
                "cookie" to "tt_webid_v2=$did"
        )

        val r = Requests.get(url)
                .headers(headers).send().readToText()

        return Gson().fromJson(r, JsonObject::class.java)
    }

    private fun externalSigner(url: String, custom_did: String? = null): List {
        val query: List = if (custom_did != null) {
            listOf(
                    BasicNameValuePair("url", url),
                    BasicNameValuePair("custom_did", custom_did)
            )

        } else {
            listOf(
                    BasicNameValuePair("url", url)
            )
        }
        val data = Requests.get("" + "?%s".format(URLEncodedUtils.format(query, "utf-8"))).send().readToText()
        val parsedData = Gson().fromJson(data, JsonObject::class.java)

        return listOf(parsedData["verifyFp"].asString, parsedData["did"].asString, parsedData["_signature"].asString, parsedData["userAgent"].asString, parsedData["referrer"].asString)
    }

    private fun processParams(params: HashMap): List {
        val region = params.getOrDefault("region", "US").toString()
        val language = params.getOrDefault("language", "en").toString()
        val proxy = params.getOrDefault("proxy", null).toString()
        val maxCount = params.getOrDefault("maxCount", "35").toString()
        val did = params.getOrDefault("did", Random.nextInt(10000, 999999999).toString()).toString()
        params["did"] = did

        return listOf(region, language, proxy, maxCount, did)
    }

    private fun addNewParams(): String {
        val query = listOf(
                BasicNameValuePair("aid", "1988"),
                BasicNameValuePair("app_name", "tiktok_web"),
                BasicNameValuePair("device_platform", "web"),
                BasicNameValuePair("referer", "https://www.tiktok.com/"),
                BasicNameValuePair("user_agent", formatNewParams(this.userAgent)),
                BasicNameValuePair("cookie_enabled", "true"),
                BasicNameValuePair("screen_width", this.width),
                BasicNameValuePair("screen_height", this.height),
                BasicNameValuePair("browser_language", this.browserLanguage),
                BasicNameValuePair("browser_platform", this.browserPlatform),
                BasicNameValuePair("browser_name", this.browserName),
                BasicNameValuePair("browser_version", this.browserVersion),
                BasicNameValuePair("browser_online", "true"),
                BasicNameValuePair("ac", "4g"),
                BasicNameValuePair("timezone_name", this.timezoneName),
                BasicNameValuePair("appId", "1233"),
                BasicNameValuePair("appType", "m"),
                BasicNameValuePair("isAndroid", "false"),
                BasicNameValuePair("isMobile", "false"),
                BasicNameValuePair("isIOS", "false"),
                BasicNameValuePair("OS", "windows"),
                BasicNameValuePair("page_referer", "https://www.tiktok.com/")
        )
        return URLEncodedUtils.format(query, "utf-8")
    }

    private fun formatNewParams(parm: String): String {
        return parm.replace("/", "%2F").replace(" ", "+").replace(";", "%3B")
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy