Merge remote-tracking branch 'origin/master'
| New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project version="4"> |
| | | <component name="AppInsightsSettings"> |
| | | <option name="selectedTabId" value="Android Vitals" /> |
| | | </component> |
| | | </project> |
| | |
| | | <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> |
| | |
| | | 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" |
| | | |
| | |
| | | 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") |
| | | ) |
| | |
| | | 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!!) |
| | |
| | | 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}/" |
| | | } |
| | | } |
| New file |
| | |
| | | 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 |
| | | ) |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | package com.shsening.autouploadvideos |
| | | |
| | | import CookieHelper |
| | | import CookieHelper.saveSessionProfileUrlCookies |
| | | import Profile |
| | | import SessionProfile |
| | | import SessionProfileManager |
| | |
| | | super.onCreate(savedInstanceState) |
| | | setContentView(R.layout.activity_session_profile) |
| | | activityReference = this |
| | | |
| | | // è·åä¼ å
¥ç SessionProfile |
| | | sessionProfile = intent.getSerializableExtra("sessionProfile") as? SessionProfile |
| | | ?: SessionProfile( |
| | |
| | | lastUploadTime = "", |
| | | profiles = mutableListOf() |
| | | ) |
| | | |
| | | if (sessionProfile.id == ""){ |
| | | isNewSessionProfile = true |
| | | sessionProfile.id = UUID.randomUUID().toString().replace("-", "") |
| | |
| | | }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 |
| | | |
| | |
| | | CookieHelper.saveSessionProfileUrlCookies( |
| | | activityReference, |
| | | sessionProfile.id, |
| | | url, |
| | | cookies |
| | | url |
| | | ) |
| | | } else { |
| | | Log.w("shsening", "No cookies found for URL: $url") |
| | |
| | | |
| | | 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() |
| | |
| | | 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) |
| | | } |
| | |
| | | val cookieManager = CookieManager.getInstance() |
| | | val cookies = cookieManager.getCookie(url) |
| | | if (cookies != null) { |
| | | CookieHelper.saveSessionProfileUrlCookies(context, sessionProfileId, url, cookies) |
| | | CookieHelper.saveSessionProfileUrlCookies(context, sessionProfileId, url) |
| | | } |
| | | } |
| | | |
| | |
| | | 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 |
| | |
| | | |
| | | } |
| | | AppConfig.UPLOADSERVERTYPE_XIAOHONGSHU -> { |
| | | val actionXiaohongshu = |
| | | ActionRedbook(this, requireContext(), threadHandler, currentUploadVideoInfo) |
| | | actionXiaohongshu.checkIfContentLoaded() |
| | | |
| | | } |
| | | |
| | |
| | | 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 |
| | |
| | | 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 |
| | |
| | | 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 |
| | | |
| | |
| | | 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 |
| | |
| | | "å¿«æ" -> { |
| | | val action = |
| | | ActionKuaishou(activityReference, threadHandler, currentTask) |
| | | action.checkIfContentLoaded() |
| | | } |
| | | "å°çº¢ä¹¦" -> { |
| | | val action = |
| | | ActionRedbook(activityReference, threadHandler, currentTask) |
| | | action.checkIfContentLoaded() |
| | | } |
| | | "è§é¢å·" -> { |
| | |
| | | CookieHelper.saveSessionProfileUrlCookies( |
| | | activityReference, |
| | | currentTask.sessionProfileId!!, |
| | | url, |
| | | cookies |
| | | url |
| | | ) |
| | | } else { |
| | | Log.w("shsening", "No cookies found for URL: $url") |
| New file |
| | |
| | | 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() |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | 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() |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | |
| | | 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" |
| | | |
| | |
| | | * 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", |
| | |
| | | // 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) |
| | |
| | | |
| | | |
| | | private fun addValueToDatePickerInput(first:Boolean, jsPath: String, targetValue: String) { |
| | | println(targetValue+"è¿æ¯æ¶é´///////////") |
| | | |
| | | var datetimes = Utils.validateAndReturnDateTime(targetValue) ?: return |
| | | |
| New file |
| | |
| | | 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 |