wangrong
2024-12-10 80fa039de88829e38be193819ffea3f4144297ad
Merge remote-tracking branch 'origin/master'
3个文件已删除
7个文件已添加
10个文件已修改
909 ■■■■■ 已修改文件
.idea/appInsightsSettings.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/deploymentTargetSelector.xml 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/other.xml 329 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/debug/app-debug.apk 补丁 | 查看 | 原始文档 | blame | 历史
app/release/app-release.apk 补丁 | 查看 | 原始文档 | blame | 历史
app/release/baselineProfiles/0/app-release.dm 补丁 | 查看 | 原始文档 | blame | 历史
app/release/baselineProfiles/1/app-release.dm 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/shsening/autouploadvideos/AppConfig.kt 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/shsening/autouploadvideos/CookieHelper.kt 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/shsening/autouploadvideos/Cookiehelper2.kt 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/shsening/autouploadvideos/SessionProfileActivity.kt 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/shsening/autouploadvideos/SessionProfileWebView.kt 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/shsening/autouploadvideos/UploadFragment.kt 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/shsening/autouploadvideos/UploadSessionActivity.kt 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/shsening/autouploadvideos/action/ActionBilibili.kt 233 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/shsening/autouploadvideos/action/ActionRedbook.kt 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/shsening/autouploadvideos/action/ActionShipinhao.kt 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/ttt.json 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test.txt 补丁 | 查看 | 原始文档 | blame | 历史
未命名 补丁 | 查看 | 原始文档 | blame | 历史
.idea/appInsightsSettings.xml
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="AppInsightsSettings">
    <option name="selectedTabId" value="Android Vitals" />
  </component>
</project>
.idea/deploymentTargetSelector.xml
@@ -4,17 +4,14 @@
    <selectionStates>
      <SelectionState runConfigName="app">
        <option name="selectionMode" value="DROPDOWN" />
        <DropdownSelection timestamp="2024-11-27T11:37:15.668942400Z">
        <DropdownSelection timestamp="2024-12-09T08:10:34.232180Z">
          <Target type="DEFAULT_BOOT">
            <handle>
              <DeviceId pluginId="Default" identifier="serial=127.0.0.1:5555;connection=03e18c1a" />
              <DeviceId pluginId="PhysicalDevice" identifier="serial=xg4hkjgmhexwdawg" />
            </handle>
          </Target>
        </DropdownSelection>
        <DialogSelection />
      </SelectionState>
      <SelectionState runConfigName="SessionProfileActivity">
        <option name="selectionMode" value="DROPDOWN" />
      </SelectionState>
    </selectionStates>
  </component>
.idea/other.xml
File was deleted
app/debug/app-debug.apk
Binary files differ
app/release/app-release.apk
Binary files differ
app/release/baselineProfiles/0/app-release.dm
Binary files differ
app/release/baselineProfiles/1/app-release.dm
Binary files differ
app/src/main/java/com/shsening/autouploadvideos/AppConfig.kt
@@ -45,7 +45,7 @@
        const val URL_UPLOAD_SERVER_DOUYIN = "https://creator.douyin.com/creator-micro/content/publish?enter_from=publish_page"
        const val URL_UPLOAD_SERVER_KUAISHOU = "https://cp.kuaishou.com/article/publish/video"
        const val URL_UPLOAD_SERVER_SHIPINHAO = "https://channels.weixin.qq.com/platform/post/create"
        const val URL_UPLOAD_SERVER_XIAOHONGSHU = "https://creator.douyin.com/creator-micro/content/publish?enter_from=publish_page"
        const val URL_UPLOAD_SERVER_XIAOHONGSHU = "https://creator.xiaohongshu.com/publish/publish?from=menu"
        const val URL_UPLOAD_SERVER_YOUTUBE = "https://creator.douyin.com/creator-micro/content/publish?enter_from=publish_page"
        const val URL_UPLOAD_SERVER_TIKTOK = "https://creator.douyin.com/creator-micro/content/publish?enter_from=publish_page"
@@ -53,7 +53,7 @@
            PlatformItem(1,"抖音",true,"https://creator.douyin.com/creator-micro/content/publish?enter_from=publish_page"),
            PlatformItem(2,"快手",true,"https://cp.kuaishou.com/article/publish/video"),
            PlatformItem(3,"视频号",true,"https://channels.weixin.qq.com/platform/post/create"),
            PlatformItem(4,"小红书",false,"https://creator.douyin.com/creator-micro/content/publish?enter_from=publish_page"),
            PlatformItem(4,"小红书",true,"https://creator.xiaohongshu.com/publish/publish?from=menu"),
            PlatformItem(5,"YouTube", false,"https://creator.douyin.com/creator-micro/content/publish?enter_from=publish_page"),
            PlatformItem(6,"TikTok", false,"https://creator.douyin.com/creator-micro/content/publish?enter_from=publish_page")
        )
app/src/main/java/com/shsening/autouploadvideos/CookieHelper.kt
@@ -3,18 +3,13 @@
import android.util.Log
import android.webkit.CookieManager
import org.json.JSONObject
object CookieHelper {
    private const val PREF_NAME = "CookieHelper"
    fun saveSessionProfileUrlCookies(context: Context, sessionProfileId: String, url: String) {
        try{
            val cookies = CookieManager.getInstance().getCookie(url)
            if (cookies != null) {
                println("这是cookie"+cookies)
                val sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
                val sessionCookiesMap = sharedPreferences.getString(sessionProfileId, "{}")
                val sessionCookies = JSONObject(sessionCookiesMap!!)
@@ -26,15 +21,17 @@
            Log.e("CookieHelper", "Failed to get cookies: ", e)
        }
    }
    fun getAllSessionProfileCookies(context: Context, sessionProfileId: String): Map<String, String> {
        val sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
        val sessionCookiesMap = sharedPreferences.getString(sessionProfileId, "{}")
        val sessionCookies = JSONObject(sessionCookiesMap!!)
        println("最终的sessioncookies是"+sessionCookies)
        println("返回的结果是:"+sessionCookies.keys().asSequence().associateWith { sessionCookies.getString(it) })
        return sessionCookies.keys().asSequence().associateWith { sessionCookies.getString(it) }
    }
    private fun getBaseUrl(url: String): String {
        val uri = Uri.parse(url)
        println("最终路径:${uri.scheme}://${uri.host}/")
        return "${uri.scheme}://${uri.host}/"
    }
}
app/src/main/java/com/shsening/autouploadvideos/Cookiehelper2.kt
New file
@@ -0,0 +1,64 @@
import android.content.Context
import android.webkit.CookieManager
import com.shsening.autouploadvideos.AppConfig
import org.json.JSONObject
object CookieHelper2 {
    private const val PREF_NAME = "CookieHelper"
    fun saveSessionProfileUrlCookies(context: Context, sessionProfileId: String, url: String, cookies: String) {
        println("进来了"+cookies)
        val sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
        val sessionCookiesMap = sharedPreferences.getString(sessionProfileId, "{}")
        val sessionCookies = JSONObject(sessionCookiesMap!!)
        sessionCookies.put(url, cookies)
        sharedPreferences.edit().putString(sessionProfileId, sessionCookies.toString()).apply()
        println("结束了")
    }
    fun getAllSessionProfileCookies(context: Context, sessionProfileId: String): Map<String, String> {
        val sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
        val sessionCookiesMap = sharedPreferences.getString(sessionProfileId, "{}")
        val sessionCookies = JSONObject(sessionCookiesMap!!)
        return sessionCookies.keys().asSequence().associateWith { sessionCookies.getString(it) }
    }
    fun removeCookiesForUrl(url: String) {
        val cookieManager = CookieManager.getInstance()
        val cookies = cookieManager.getCookie(url) // èŽ·å–ç‰¹å®š URL çš„æ‰€æœ‰ Cookie
        if (cookies != null) {
            val cookieArray = cookies.split(";") // æŒ‰åˆ†å·åˆ†éš”多个 Cookie
            for (cookie in cookieArray) {
                val cookieParts = cookie.split("=")
                if (cookieParts.size > 0) {
                    val cookieName = cookieParts[0].trim()
                    // è®¾ç½®ä¸€ä¸ªè¿‡æœŸçš„ Cookie è¦†ç›–原来的
                    cookieManager.setCookie(url, "$cookieName=; Expires=Thu, 01 Jan 1970 00:00:00 GMT")
                }
            }
            // ç¡®ä¿å°†æ›´æ”¹åŒæ­¥åˆ°å­˜å‚¨ä¸­
            cookieManager.flush()
        }
    }
    fun removeCookieForAllPlatform(){
        AppConfig.SESSION_PROFILE_PALTFORM_LISTS.forEach { item ->
            removeCookiesForUrl(item.url)
        }
    }
    fun saveSessionCookies(context: Context,sessionProfileId:String) {
        var cookieManager = CookieManager.getInstance()
        AppConfig.SESSION_PROFILE_PALTFORM_LISTS.forEach { item ->
            val cookies = cookieManager.getCookie(item.url)
            if (cookies != null) {
                saveSessionProfileUrlCookies(
                    context,
                    sessionProfileId,
                    item.url,
                    cookies
                )
            }
        }
    }
}
app/src/main/java/com/shsening/autouploadvideos/SessionProfileActivity.kt
@@ -1,7 +1,6 @@
package com.shsening.autouploadvideos
import CookieHelper
import CookieHelper.saveSessionProfileUrlCookies
import Profile
import SessionProfile
import SessionProfileManager
@@ -46,7 +45,6 @@
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_session_profile)
        activityReference = this
        // èŽ·å–ä¼ å…¥çš„ SessionProfile
        sessionProfile = intent.getSerializableExtra("sessionProfile") as? SessionProfile
            ?: SessionProfile(
@@ -56,7 +54,6 @@
                lastUploadTime = "",
                profiles = mutableListOf()
            )
        if (sessionProfile.id == ""){
            isNewSessionProfile = true
            sessionProfile.id = UUID.randomUUID().toString().replace("-", "")
@@ -64,7 +61,6 @@
        }else {
            findViewById<TextView>(R.id.txt_session_id).text = sessionProfile.id
        }
        editName = findViewById(R.id.edit_name)
        findViewById<TextView>(R.id.edit_name).text = sessionProfile.name
@@ -124,8 +120,7 @@
                            CookieHelper.saveSessionProfileUrlCookies(
                                activityReference,
                                sessionProfile.id,
                                url,
                                cookies
                                url
                            )
                        } else {
                            Log.w("shsening", "No cookies found for URL: $url")
@@ -207,7 +202,6 @@
    private fun saveSessionProfile() {
        try {
            CookieHelper.saveSessionCookies(this,sessionProfile.id)
            val name = editName.text.toString().trim() // èŽ·å–è¾“å…¥çš„åç§°
            if (name.isEmpty()) {
                Toast.makeText(this, "Name cannot be empty!", Toast.LENGTH_SHORT).show()
app/src/main/java/com/shsening/autouploadvideos/SessionProfileWebView.kt
@@ -46,6 +46,7 @@
        CookieManager.getInstance().removeAllCookies(null)
        CookieManager.getInstance().flush()
        val sessionCookies = CookieHelper.getAllSessionProfileCookies(context, sessionProfileId)
        println("这是session cookie"+sessionCookies)
        for ((url, cookies) in sessionCookies) {
            splitCookieStringAndSetCookie(url,cookies)
        }
@@ -69,7 +70,7 @@
        val cookieManager = CookieManager.getInstance()
        val cookies = cookieManager.getCookie(url)
        if (cookies != null) {
            CookieHelper.saveSessionProfileUrlCookies(context, sessionProfileId, url, cookies)
            CookieHelper.saveSessionProfileUrlCookies(context, sessionProfileId, url)
        }
    }
app/src/main/java/com/shsening/autouploadvideos/UploadFragment.kt
@@ -22,6 +22,7 @@
import com.shsening.autouploadvideos.action.ActionDouyin
import com.shsening.autouploadvideos.action.ActionKuaishou
import com.shsening.autouploadvideos.action.ActionShipinhao
import com.shsening.autouploadvideos.action.ActionRedbook
import com.shsening.autouploadvideos.utils.Utils
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
@@ -169,6 +170,9 @@
                }
                AppConfig.UPLOADSERVERTYPE_XIAOHONGSHU -> {
                    val actionXiaohongshu =
                        ActionRedbook(this, requireContext(), threadHandler, currentUploadVideoInfo)
                    actionXiaohongshu.checkIfContentLoaded()
                }
app/src/main/java/com/shsening/autouploadvideos/UploadSessionActivity.kt
@@ -1,11 +1,10 @@
package com.shsening.autouploadvideos
import CookieHelper
import Profile
import CookieHelper2
import SessionProfile
import SessionProfileManager
import android.app.Activity
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.os.Handler
@@ -17,13 +16,7 @@
import android.webkit.WebChromeClient
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.Spinner
import android.widget.Switch
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
@@ -31,27 +24,18 @@
import com.shsening.autouploadvideos.AppConfig.Companion.SESSION_PROFILE_PALTFORM_LISTS
import com.shsening.autouploadvideos.action.ActionDouyin
import com.shsening.autouploadvideos.action.ActionKuaishou
import com.shsening.autouploadvideos.action.ActionRedbook
import com.shsening.autouploadvideos.action.ActionShipinhao
import com.shsening.autouploadvideos.utils.DownloadHelper
import com.shsening.autouploadvideos.utils.DownloadReceiver
import com.shsening.autouploadvideos.utils.Utils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okhttp3.Call
import okhttp3.Callback
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.json.JSONArray
import java.io.File
import java.io.IOException
import java.util.LinkedList
import java.util.Queue
import java.util.UUID
class UploadSessionActivity : AppCompatActivity() {
    private var RedbooklUrl: String? = "https://creator.xiaohongshu.com/publish/publish?from=menu"
    private var DingyuehaolUrl: String? = null
    private lateinit var sessionProfileManager: SessionProfileManager
    lateinit var sessionProfileWebView:SessionProfileWebView
@@ -99,11 +83,12 @@
            override fun onPageStarted(
                view: WebView?,
                url: String?,
                favicon: android.graphics.Bitmap?
                favicon: Bitmap?
            ) {
                super.onPageStarted(view, url, favicon)
            }
            override fun onPageFinished(view: WebView?, url: String?) {
                println("这是其他页面"+url)
                super.onPageFinished(view, url)
                view?.requestFocus() // ç¡®ä¿ WebView èŽ·å–ç„¦ç‚¹
                //inject action
@@ -117,6 +102,11 @@
                    "快手" -> {
                        val action =
                            ActionKuaishou(activityReference, threadHandler, currentTask)
                        action.checkIfContentLoaded()
                    }
                    "小红书" -> {
                        val action =
                            ActionRedbook(activityReference, threadHandler, currentTask)
                        action.checkIfContentLoaded()
                    }
                    "视频号" -> {
@@ -133,8 +123,7 @@
                            CookieHelper.saveSessionProfileUrlCookies(
                                activityReference,
                                currentTask.sessionProfileId!!,
                                url,
                                cookies
                                url
                            )
                        } else {
                            Log.w("shsening", "No cookies found for URL: $url")
app/src/main/java/com/shsening/autouploadvideos/action/ActionBilibili.kt
New file
@@ -0,0 +1,233 @@
package com.shsening.autouploadvideos.action
import android.app.Instrumentation
import android.content.Context
import android.os.SystemClock
import android.os.Handler
import com.shsening.autouploadvideos.UploadSessionActivity
import com.shsening.autouploadvideos.utils.Utils
import com.shsening.autouploadvideos.WebviewUtils
import com.shsening.autouploadvideos.action.PlatformActionInterface
class ActionBilibili (private var activity: UploadSessionActivity, private var threadHandler: Handler, private var videoInfo: Utils.VideoUploadTaskInfo) :
    PlatformActionInterface {
    private var webView = activity.sessionProfileWebView
    var jsCode_CheckContentLoaded = """
        document.querySelector('#video-up-app > div.video-entrance > div.upload-body > div > div.upload-wrp > div.bcc-upload.upload > div > input[type=file]') !== null
    """.trimIndent()
    var jsCode_CheckVideoUploadFinished = """
        document.querySelector('#video-up-app > div.video-basic-wrp > div.video-basic > div.video-upload > div.upload-queue > div > div.file-list > div.drag-list > div > div > div.file-item-content > div.file-item-content-detail > div.file-item-content-operate > div > div') !==null
    """.trimIndent()
    var jsCode_PublishVideo_Finished ="document.querySelector('#video-up-app > div.video-entrance > div.upload-body > div > div.banner-entry > div.banner-entry-right > div') !== null"
    var jsPath_PublishVideo ="#video-up-app > div.video-basic-wrp > div.video-basic > div.form > div:nth-child(17) > div > div > span"
    private fun updatePostDescription(){
        WebviewUtils.addValueToInput(webView,"#video-up-app > div.video-basic-wrp > div.video-basic > div.form > div.form-item.form-item-short-margin > div > div.video-title-content > div.input.input-container > div > input",videoInfo.title+videoInfo.datetime)
        WebviewUtils.addValueToDiv(webView,"#video-up-app > div.video-basic-wrp > div.video-basic > div.form > div:nth-child(7) > div > div.desc-text-wrp > div > div.editor.ql-container > div.ql-editor.ql-blank",videoInfo.description)
        WebviewUtils.simpleElementClick(webView,"#video-up-app > div.video-basic-wrp > div.video-basic > div.form > div:nth-child(8) > div > div.time-switch-wrp > div")
        val datetime1 = videoInfo.datetime.substring(0,8)
        val datetime2 = videoInfo.datetime.substring(8)
        setDatePickerInput("#video-up-app > div.video-basic-wrp > div.video-basic > div.form > div:nth-child(8) > div > div.time-switch-wrp > div.time-picker > div > div > div:nth-child(2) > div",datetime1)
        setTimePickerInput("#video-up-app > div.video-basic-wrp > div.video-basic > div.form > div:nth-child(8) > div > div.time-switch-wrp > div.time-picker > div > div > div:nth-child(3) > div",datetime2)
        // åœ¨ä¸»çº¿ç¨‹ä¸­è®¾ç½®å»¶è¿Ÿæ‰§è¡Œ
        threadHandler.postDelayed(Runnable {
            startToPublishVideo()
        }, 5000)
    }
    override fun checkIfContentLoaded() {
        webView.postDelayed({
            webView.evaluateJavascript(jsCode_CheckContentLoaded) { result ->
                if (result == "true") {
                    simulateSwipeAndUploadVideo()
                } else {
                    webView.postDelayed({
                        checkIfContentLoaded()
                    }, 1000) // 1秒后再次检查
                }
            }
        }, 1000) // æ¯ç§’检查一次
    }
    private fun startToPublishVideo(){
        WebviewUtils.simpleElementClick(webView, jsPath_PublishVideo)
        checkVideoPublishFinished()
    }
    private fun checkVideoUploadFinished(){
        webView.postDelayed({
            webView.evaluateJavascript(jsCode_CheckVideoUploadFinished) { result ->
                if (result == "true") {
                    // ç­‰å¾…视频检测和生成封面
                    threadHandler.postDelayed({updatePostDescription()},3000)
                } else {
                    // å…ƒç´ ä¸å­˜åœ¨ï¼Œ1秒后再次检查
                    webView.postDelayed({
                        checkVideoUploadFinished()
                    }, 1000) // 1秒后再次检查
                }
            }
        }, 1000) // æ¯ç§’检查一次
    }
    private fun checkVideoPublishFinished(){
        webView.postDelayed({
            webView.evaluateJavascript(jsCode_PublishVideo_Finished) { result ->
                if (result == "true") {
                    // video published successfully!!!
                    activity.checkAndStartNextTask()
                } else {
                    // å…ƒç´ ä¸å­˜åœ¨ï¼Œ1秒后再次检查
                    webView.postDelayed({
                        checkVideoPublishFinished()
                    }, 1000) // 1秒后再次检查
                }
            }
        }, 1000) // æ¯ç§’检查一次
    }
    private fun setDatePickerInput(jsPath: String, targetValue: String) {
        var datetimes = Utils.validateAndReturnDateTime(targetValue) ?: return
        var dpPanelText = datetimes[0].toString()+"å¹´"+datetimes[1].toString()+"月";
        var dateRowColum = Utils.getDatePosition(datetimes[0],datetimes[1],datetimes[2])?:return;
        val jsCode = """
       try {
           var dp = document.querySelector('${jsPath}');
           dp.focus(); // ç¡®ä¿è¾“入框获得焦点
           dp.click();
           // need time to popup date-picker
           setTimeout(() => {
               var dpPanel = document.querySelector('.date-picker-container');
               var dpPanelSpan = dpPanel.querySelector('div > p');
               if (dpPanelSpan && dpPanelSpan.textContent != '${dpPanelText}'){
                  document.querySelector('#video-up-app > div.video-basic-wrp > div.video-basic > div.form > div:nth-child(8) > div > div.time-switch-wrp > div.time-picker > div > div > div:nth-child(2) > div.date-picker-container > div.date-picker-nav-wrp > svg.next-btn-day.icon-sprite.icon-sprite-ic_into').click();
               }
               document.querySelector('div > div > div > div > div:nth-child(1) > div.semi-datepicker-month > div.semi-datepicker-weeks > div:nth-child(${dateRowColum[0]}) > div:nth-child(${dateRowColum[1]})').click()
               //switch to time selection
               document.querySelector('#video-up-app > div.video-basic-wrp > div.video-basic > div.form > div:nth-child(8) > div > div.time-switch-wrp > div.time-picker > div > div > div:nth-child(2) > div.date-picker-container > div.date-picker-nav-wrp > svg.next-btn-day.icon-sprite.icon-sprite-ic_into').click();
               setTimeout(() => {
                  dpPanel.querySelector('div > div > div > div.semi-datepicker-tpk > div > div > div.semi-scrolllist-body > div.semi-scrolllist-item-wheel.undefined-list-hour > div.semi-scrolllist-list-outer > ul > li:nth-child(${(datetimes[3]+1)})').click();
                  dpPanel.querySelector('div > div > div > div.semi-datepicker-tpk > div > div > div.semi-scrolllist-body > div.semi-scrolllist-item-wheel.undefined-list-minute > div.semi-scrolllist-list-outer > ul > li:nth-child(${(datetimes[4]+1)})').click();
               }, 500);
           }, 500);
            } catch (error) {
                console.error('设置日期时发生错误:', error);
            }
        """.trimIndent()
        // åœ¨ WebView ä¸­æ‰§è¡Œ JavaScript ä»£ç 
        webView.evaluateJavascript(jsCode, null)
    }
    private fun setTimePickerInput(jsPath: String, targetValue: String) {
        // è§£æžåˆ†ç§’字符串,假设格式为 "MMSS"
        val minutes = targetValue.substring(0, 2).toIntOrNull() ?: return // èŽ·å–å‰ä¸¤ä¸ªå­—ç¬¦ä½œä¸ºåˆ†é’Ÿï¼Œå¦‚æžœè½¬æ¢å¤±è´¥åˆ™è¿”å›ž
        val seconds = targetValue.substring(2, 4).toIntOrNull() ?: return // èŽ·å–åŽä¸¤ä¸ªå­—ç¬¦ä½œä¸ºç§’ï¼Œå¦‚æžœè½¬æ¢å¤±è´¥åˆ™è¿”å›ž
        // æž„建要执行的 JavaScript ä»£ç 
        val jsCode = """
            try {
                setTimeout(() => {
                    var minuteSelector = document.querySelector('.minute-selector-class');
                    var secondSelector = document.querySelector('.second-selector-class');
                    if (minuteSelector && secondSelector) {
                        minuteSelector.value = ${minutes};
                        secondSelector.value = ${seconds};
                    }
                }, 500); // å»¶è¿Ÿæ—¶é—´å¯èƒ½éœ€è¦æ ¹æ®å®žé™…情况调整
            } catch (error) {
                console.error('设置时间时发生错误:', error);
            }
        """.trimIndent()
        // åœ¨ WebView ä¸­æ‰§è¡Œ JavaScript ä»£ç 
        webView.evaluateJavascript(jsCode, null)
    }
    private fun addValueToDatePickerInput(first:Boolean, jsPath: String, targetValue: String) {
        var datetimes = Utils.validateAndReturnDateTime(targetValue) ?: return
        var dpPanelText = datetimes[0].toString()+"å¹´ "+datetimes[1].toString()+"月";
        var dateRowColum = Utils.getDatePosition(datetimes[0], datetimes[1], datetimes[2]) ?:return;
        val jsCode = """
       try {
           var dp = document.querySelector('${jsPath}');
           dp.focus(); // ç¡®ä¿è¾“入框获得焦点
           dp.click();
           // need time to popup date-picker
           setTimeout(() => {
               var dpPanel = document.querySelector('.semi-popover-content');
               var dpPanelSpan = dpPanel.querySelector('div > div > div > div:nth-child(1) > div.semi-datepicker-navigation > div > button > span > span');
               if (dpPanelSpan && dpPanelSpan.textContent != '${dpPanelText}'){
                  dpPanel.querySelector('div > div > div > div > div:nth-child(1) > div.semi-datepicker-navigation > button:nth-child(3)').click();
               }
               dpPanel.querySelector('div > div > div > div > div:nth-child(1) > div.semi-datepicker-month > div.semi-datepicker-weeks > div:nth-child(${dateRowColum[0]}) > div:nth-child(${dateRowColum[1]})').click()
               //switch to time selection
               dpPanel.querySelector('div > div > div > div > div.semi-datepicker-switch > div.semi-datepicker-switch-time').click();
               setTimeout(() => {
                  dpPanel.querySelector('div > div > div > div.semi-datepicker-tpk > div > div > div.semi-scrolllist-body > div.semi-scrolllist-item-wheel.undefined-list-hour > div.semi-scrolllist-list-outer > ul > li:nth-child(${(datetimes[3]+1)})').click();
                  dpPanel.querySelector('div > div > div > div.semi-datepicker-tpk > div > div > div.semi-scrolllist-body > div.semi-scrolllist-item-wheel.undefined-list-minute > div.semi-scrolllist-list-outer > ul > li:nth-child(${(datetimes[4]+1)})').click();
               }, 500);
           }, 500);
       } catch (error) {
        console.error('发生错误:', error);
       }
    """
        webView.evaluateJavascript(jsCode,null)
    }
    private fun simulateSwipeAndUploadVideo() {
        val instrumentation = Instrumentation()
        val displayMetrics = activity.resources.displayMetrics
        val width = displayMetrics.widthPixels
        val height = displayMetrics.heightPixels
        val startX = width.toFloat() * 0.9f // ä»Žå³ä¾§æ»‘动
        val endX = width.toFloat() * 0.1f // æ»‘动到左侧
        val y = height / 3f // Y åæ ‡åœ¨å±å¹•中间
        Thread {
            Utils.instrumentationSwipe(instrumentation, startX, y, endX, y)
            SystemClock.sleep(500)
            Utils.instrumentationSwipe(instrumentation, startX, y, endX, y)
            SystemClock.sleep(500)
            Utils.instrumentationClick(instrumentation, width / 2f, height * 2 / 4f)
            // back to main thread
            threadHandler.post {
                checkVideoUploadFinished()
            }
        }.start()
    }
}
app/src/main/java/com/shsening/autouploadvideos/action/ActionRedbook.kt
New file
@@ -0,0 +1,168 @@
package com.shsening.autouploadvideos.action
import android.app.Instrumentation
import android.content.Context
import android.os.SystemClock
import android.os.Handler
import android.os.Bundle
import android.os.Looper
import androidx.appcompat.app.AppCompatActivity
import com.shsening.autouploadvideos.UploadSessionActivity
import com.shsening.autouploadvideos.WebviewUtils
import com.shsening.autouploadvideos.action.PlatformActionInterface
import com.shsening.autouploadvideos.utils.Utils
class ActionRedbook (private var activity: UploadSessionActivity, private var threadHandler: Handler, private var videoInfo: Utils.VideoUploadTaskInfo):
    PlatformActionInterface {
    private var webView = activity.sessionProfileWebView
    var jsCode_CheckContentLoaded = """
        document.querySelector('input[type="file"][class="upload-input"]') !== null
    """.trimIndent()
    var jsCode_CheckVideoUploadFinished = """
        document.querySelector("#publish-container > div.post-page > div.body > div.content > div.media-area-new > div > div > div.content > div.operator > span:nth-child(1)") !==null
    """.trimIndent()
    var jsCode_PublishVideo_Finished ="document.querySelector('#web > div > div > div > div.upload-content > div.tips-wrapper > div:nth-child(3) > div > p:nth-child(3)') !== null"
    var jsPath_PublishVideo ="button.publishBtn"
    private fun updatePostDescription(){
        WebviewUtils.addValueToInput(webView,"#publish-container > div > div.body > div.content > div.input.titleInput > div.d-input-wrapper.d-inline-block.c-input_inner > div > input",videoInfo.title)
        WebviewUtils.addValueToDiv(webView,"#quillEditor > div > p",videoInfo.description)
        WebviewUtils.simpleElementClick(webView,"html > body > div:nth-of-type(1) > div > div:nth-of-type(2) > div > div:nth-of-type(2) > main > div:nth-of-type(3) > div > div > div:nth-of-type(1) > div > div > div > div:nth-of-type(2) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(10) > div > div:nth-of-type(5) > div:nth-of-type(2) > label:nth-of-type(2)")
        addValueToDatePickerInput(true,"html > body > div:nth-of-type(1) > div > div:nth-of-type(2) > div > div:nth-of-type(2) > main > div:nth-of-type(3) > div > div > div:nth-of-type(1) > div > div > div > div:nth-of-type(2) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(10) > div > div:nth-of-type(5) > div:nth-of-type(2) > div > div:nth-of-type(1) > div",videoInfo.datetime)
        // åœ¨ä¸»çº¿ç¨‹ä¸­è®¾ç½®å»¶è¿Ÿæ‰§è¡Œ
        threadHandler.postDelayed(Runnable {
            startToPublishVideo()
        }, 5000)
    }
    override fun checkIfContentLoaded() {
        webView.postDelayed({
            webView.evaluateJavascript(jsCode_CheckContentLoaded) { result ->
                if (result == "true") {
                    simulateSwipeAndUploadVideo()
                } else {
                    webView.postDelayed({
                        checkIfContentLoaded()
                    }, 1000) // 1秒后再次检查
                }
            }
        }, 1000) // æ¯ç§’检查一次
    }
    private fun startToPublishVideo(){
        WebviewUtils.simpleElementClick(webView,jsPath_PublishVideo)
        checkVideoPublishFinished()
    }
    private fun checkVideoUploadFinished(){
        webView.postDelayed({
            webView.evaluateJavascript(jsCode_CheckVideoUploadFinished) { result ->
                if (result == "true") {
                    // ç­‰å¾…视频检测和生成封面
//                    WebviewUtils.simpleElementClick(webView,"#web > div > div > div > div.upload-content > div.upload-wrapper > div > input")
                    println("33333333333333333333333333333333333333")
                    threadHandler.postDelayed({updatePostDescription()},3000)
                } else {
                    println("11111111111111111111111")
                    // å…ƒç´ ä¸å­˜åœ¨ï¼Œ1秒后再次检查
                    webView.postDelayed({
                        checkVideoUploadFinished()
                    }, 1000) // 1秒后再次检查
                }
            }
        }, 1000) // æ¯ç§’检查一次
    }
    private fun checkVideoPublishFinished(){
        webView.postDelayed({
            webView.evaluateJavascript(jsCode_PublishVideo_Finished) { result ->
                if (result == "true") {
                    // video published successfully!!!
                    activity.checkAndStartNextTask()
                } else {
                    // å…ƒç´ ä¸å­˜åœ¨ï¼Œ1秒后再次检查
                    webView.postDelayed({
                        checkVideoPublishFinished()
                    }, 1000) // 1秒后再次检查
                }
            }
        }, 1000) // æ¯ç§’检查一次
    }
    private fun addValueToDatePickerInput(first:Boolean, jsPath: String, targetValue: String) {
        var datetimes = Utils.validateAndReturnDateTime(targetValue) ?: return
        var dpPanelText = datetimes[0].toString()+"å¹´ "+datetimes[1].toString()+"月";
        var dateRowColum = Utils.getDatePosition(datetimes[0],datetimes[1],datetimes[2])?:return;
        val jsCode = """
       try {
           var dp = document.querySelector('${jsPath}');
           dp.focus(); // ç¡®ä¿è¾“入框获得焦点
           dp.click();
           // need time to popup date-picker
           setTimeout(() => {
               var dpPanel = document.querySelector('.semi-popover-content');
               var dpPanelSpan = dpPanel.querySelector('div > div > div > div:nth-child(1) > div.semi-datepicker-navigation > div > button > span > span');
               if (dpPanelSpan && dpPanelSpan.textContent != '${dpPanelText}'){
                  dpPanel.querySelector('div > div > div > div > div:nth-child(1) > div.semi-datepicker-navigation > button:nth-child(3)').click();
               }
               dpPanel.querySelector('div > div > div > div > div:nth-child(1) > div.semi-datepicker-month > div.semi-datepicker-weeks > div:nth-child(${dateRowColum[0]}) > div:nth-child(${dateRowColum[1]})').click()
               //switch to time selection
               dpPanel.querySelector('div > div > div > div > div.semi-datepicker-switch > div.semi-datepicker-switch-time').click();
               setTimeout(() => {
                  dpPanel.querySelector('div > div > div > div.semi-datepicker-tpk > div > div > div.semi-scrolllist-body > div.semi-scrolllist-item-wheel.undefined-list-hour > div.semi-scrolllist-list-outer > ul > li:nth-child(${(datetimes[3]+1)})').click();
                  dpPanel.querySelector('div > div > div > div.semi-datepicker-tpk > div > div > div.semi-scrolllist-body > div.semi-scrolllist-item-wheel.undefined-list-minute > div.semi-scrolllist-list-outer > ul > li:nth-child(${(datetimes[4]+1)})').click();
               }, 500);
           }, 500);
       } catch (error) {
        console.error('发生错误:', error);
       }
    """
        webView.evaluateJavascript(jsCode,null)
    }
    private fun simulateSwipeAndUploadVideo() {
        val instrumentation = Instrumentation()
        val displayMetrics = activity.resources.displayMetrics
        val width = displayMetrics.widthPixels
        val height = displayMetrics.heightPixels
        val startX = width.toFloat() * 0.9f // ä»Žå³ä¾§æ»‘动
        val endX = width.toFloat() * 0.1f // æ»‘动到左侧
        val y = height / 3f // Y åæ ‡åœ¨å±å¹•中间
        Thread {
            Utils.instrumentationSwipe(instrumentation, startX, y, endX, y)
            SystemClock.sleep(500)
            Utils.instrumentationSwipe(instrumentation, startX, y, endX, y)
            SystemClock.sleep(500)
            Utils.instrumentationClick(instrumentation, width / 2f, height * 2 / 4f)
            // back to main thread
            threadHandler.post {
                checkVideoUploadFinished()
            }
        }.start()
    }
}
app/src/main/java/com/shsening/autouploadvideos/action/ActionShipinhao.kt
@@ -16,12 +16,12 @@
    var jsCode_CheckContentLoaded = """
        document.querySelector("#container-wrap > div.container-center > div > div > div.main-body-wrap.post-create > div.main-body > div > div.post-edit-wrap.material-edit-wrap > div.material > div > div > div > span > div") !== null
        document.querySelector("#container-wrap > div.container-center > div > div > div.main-body-wrap.post-create > div.main-body > div > div.post-edit-wrap.material-edit-wrap > div.material > div > div > div > span > div > span > input[type=file]") !== null
    """.trimIndent()
    var jsCode_CheckVideoUploadFinished = """
        document.querySelector("#container-wrap > div.container-center > div > div > div.main-body-wrap.post-create > div.main-body > div > div.post-edit-wrap.material-edit-wrap > div.material > div.preview > div > div.media-opr > div") !==null
        document.querySelector("#container-wrap > div.container-center > div > div > div.main-body-wrap.post-create > div.main-body > div > div.post-edit-wrap.material-edit-wrap > div.form > div:nth-child(1) > div.form-item-body > div > div.video-cover > div.weui-desktop-popover__wrp.pure-text-popover > span > div") !==null
    """.trimIndent()
    var jsCode_PublishVideo_Finished ="document.querySelector('#container-wrap > div.container-center > div > div.main-body-wrap > div.main-body > div.header-wrap > span') !==null"
    var jsCode_PublishVideo_Finished ="document.querySelector('#footer > div.main-part > div.left-links > a:nth-child(3)') !==null"
    var jsPath_PublishVideo ="#container-wrap > div.container-center > div > div > div.main-body-wrap.post-create > div.main-body > div > div.post-edit-wrap.material-edit-wrap > div.form > div.form-btns > div:nth-child(5) > span > div > button"
@@ -31,6 +31,7 @@
     * Title,description(including # and @ ) publish datetime etc
     */
    private fun updatePostDescription(){
        WebviewUtils.addValueToInput(webView,"#container-wrap > div.container-center > div > div > div.main-body-wrap.post-create > div.main-body > div > div.post-edit-wrap.material-edit-wrap > div.form > div:nth-child(8) > div.form-item-body.short-title-wrap > div > div > span > input",videoInfo.title)
        WebviewUtils.addValueToDiv(
            webView,
            "#container-wrap > div.container-center > div > div > div.main-body-wrap.post-create > div.main-body > div > div.post-edit-wrap.material-edit-wrap > div.form > div:nth-child(2) > div.form-item-body > div > div.input-editor",
@@ -41,7 +42,7 @@
        // publish later radiobox
        WebviewUtils.simpleElementClick(
            webView,
            "#container-wrap > div.container-center > div > div > div.main-body-wrap.post-create > div.main-body > div > div.post-edit-wrap.material-edit-wrap > div.form > div.post-time-wrap > div:nth-child(1) > div.form-item-body > div > label:nth-child(2)"
            "#container-wrap > div.container-center > div > div > div.main-body-wrap.post-create > div.main-body > div > div.post-edit-wrap.material-edit-wrap > div.form > div.post-time-wrap > div > div.form-item-body > div > label:nth-child(2)"
        )
        addValueToDatePickerInput(true,"#container-wrap > div.container-center > div > div > div.main-body-wrap.post-create > div.main-body > div > div.post-edit-wrap.material-edit-wrap > div.form > div.post-time-wrap > div:nth-child(2) > div.form-item-body > dl > dd",videoInfo.datetime)
@@ -106,6 +107,7 @@
    private fun addValueToDatePickerInput(first:Boolean, jsPath: String, targetValue: String) {
        println(targetValue+"这是时间///////////")
        var datetimes = Utils.validateAndReturnDateTime(targetValue) ?: return
app/ttt.json
New file
@@ -0,0 +1,23 @@
xsecappid=ugc;
a1=1939a580098qal8prsprpx71cj9rygo65cs0ame6n30000165115;
webId=3fe950abc269d8ccc08d8ab1a513b51e;
gid=yjqj02Yyqf2Kyjqj02Y88S6djYA01YUFMUFUMKICWySVj8q8FvklK0888yK2yy288YqdW84f;
customer-sso-sid=68c517445160666216132753128a94cf26ffda46;
x-user-id-creator.xiaohongshu.com=66f9208b000000001d0307a5;
customerClientId=496382327268371;
access-token-creator.xiaohongshu.com=customer.creator.AT-68c517446276627574625368zz58vqokvj20qob0;
galaxy_creator_session_id=NrzaB1vOCZl5RrtKdOBmW7Glu0Z9uQAz6uWh;
galaxy.creator.beaker.session.id=1733721380817008025744;
acw_tc=0a4a9a7a17337344438126430e8b239cf9bb9e8ccd29cd7c5128375c1e1baa;
websectiga=29098a4cf41f76ee3f8db19051aaa60c0fc7c5e305572fec762da32d457d76ae;
sec_poison_id=fc7e7662-9729-4ea2-80fe-0d9f21445919
xsecappid=ugc;
acw_tc=0a4a1d1e17338016839177906e1b35470f735fdbfbd6b1e5910b33b5940d03;
websectiga=2a3d3ea002e7d92b5c9743590ebd24010cf3710ff3af8029153751e41a6af4a3;
sec_poison_id=4a974805-0f17-4f34-aace-af303de48018;
gid=yjq0d0J4yKF4yjq000280M1SS8fvd3hv1Fl8VCI6D343T828yhSY0h888q8qq2Y8d8y20JqS
test.txt
δÃüÃû
Binary files differ