下载抖音直播视频功能,代码比较简单,参考了某博客用python实现的的下载功能,原文地址:https://www.cnblogs.com/wuyongcong/p/12627370.html
第一步:获取抖音直播分享短链接对应的长链接
fun startALaunch(baseUrl: String, onConnectionCallBack: OnConnectionCallBack) {
job = GlobalScope.launch {
var con: HttpURLConnection = URL(baseUrl).openConnection() as HttpURLConnection
con.instanceFollowRedirects = false
con.connectTimeout = 5000
val url: String = con.getHeaderField("Location")
con.disconnect()
onConnectionCallBack.onBack(url)
}
}
第二部:通过接口,请求直播推流地址fun getRtmpPullUrl(roomID: String) {
if(!TextUtils.isEmpty(roomID)) {
OkGo.get<String>("https://webcast-hl.amemv.com/webcast/room/reflow/info/")
.params("room_id", roomID)
.params("live_id", 1)
.execute(object : StringCallback() {
override fun onSuccess(response: Response<String>?) {
if (response != null) {
logged("response:" + response.body())
var data =
getMap(response.body().toString()).get("data") as Map<String, Any>
var room = data.get("room") as Map<String, Any>
var stream_url = room.get("stream_url") as Map<String, Any>
var rtmp_pull_url = stream_url.get("rtmp_pull_url")
var msg = Message()
msg.what = 4
msg.obj = rtmp_pull_url
handler.sendMessage(msg)
}
}
})
}
}
完整代码:class MainActivity : AppCompatActivity() {
var job: Job? = null
val re = Regex("""(\d{19})""")
val gson = Gson()
var handler = object :Handler(){
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when(msg.what){
0 ->{
textview.setText("直播短链接:"+msg.obj)
}
1 ->{
initPostUrl(msg.obj as String)
}
2 ->{
initRtmpPullUrl(msg.obj as String)
}
3 ->{
getRtmpPullUrl(msg.obj as String)
}
4 ->{
textview3.setText("推流地址:${msg.obj}")
startForBrowser(msg.obj as String)
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main )
initBaseString()
initRid()
}
/**
* 获取直播短链接
*/
fun initBaseString(){
edittext.addTextChangedListener(object :TextWatcher{
override fun afterTextChanged(s: Editable?) {
var baseString = edittext.text.toString()
//判断分享过来的是否是抖音的短链接
if(baseString.contains(" https://v.douyin.com")) {
var msg = Message()
msg.what = 0;
msg.obj = baseString.split("!")[1] //拆分分享短连接的文案、链接
handler.sendMessage(msg)
}
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
}
/**
* 获取直播长链接
*/
fun initRid(){
button.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
var rid: String = textview.text.toString().replace("直播短链接:","")
if(!TextUtils.isEmpty(rid)) {
var msg = Message()
msg.what = 1
msg.obj = rid
handler.sendMessage(msg)
}
}
})
}
fun startForBrowser(tempUrl:String){
if(!TextUtils.isEmpty(tempUrl)) {
textview3.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
var uri = Uri.parse(tempUrl)
var intent = Intent(Intent.ACTION_VIEW, uri)
startActivity(intent)
}
})
}
}
/**
* 获取请求链接地址
*/
fun initPostUrl(rid:String){
if(!TextUtils.isEmpty(rid)) {
startALaunch(rid, object : OnConnectionCallBack {
override fun onBack(string: String) {
if (job != null) {
job!!.cancel()
}
var msg = Message()
msg.what = 2
msg.obj = string
handler.sendMessage(msg)
}
})
}
}
/**
* 获取推流地址
*/
fun initRtmpPullUrl(postUrl:String ){
if(!TextUtils.isEmpty(postUrl)) {
var room_id = re.findAll(postUrl)
room_id.forEach {
var msg = Message()
msg.what = 3
msg.obj = it.value
handler.sendMessage(msg)
}
}
}
/**
* 请求推流地址的接口
*/
fun getRtmpPullUrl(roomID: String) {
if(!TextUtils.isEmpty(roomID)) {
OkGo.get<String>("https://webcast-hl.amemv.com/webcast/room/reflow/info/")
.params("room_id", roomID)
.params("live_id", 1)
.execute(object : StringCallback() {
override fun onSuccess(response: Response<String>?) {
if (response != null) {
logged("response:" + response.body())
var data =
getMap(response.body().toString()).get("data") as Map<String, Any>
var room = data.get("room") as Map<String, Any>
var stream_url = room.get("stream_url") as Map<String, Any>
var rtmp_pull_url = stream_url.get("rtmp_pull_url")
var msg = Message()
msg.what = 4
msg.obj = rtmp_pull_url
handler.sendMessage(msg)
}
}
})
}
}
/**
* 解析获取推流地址接口返回的数据
*/
fun getMap(json: String): Map<String, Any> {
var map = gson.fromJson<Map<String, Any>>(
json,
object : TypeToken<Map<String, Any>>() {}.type
) as Map<String, Any>
return map
}
/**
* 解析成长链接
*/
fun startALaunch(baseUrl: String, onConnectionCallBack: OnConnectionCallBack) {
job = GlobalScope.launch {
var con: HttpURLConnection = URL(baseUrl).openConnection() as HttpURLConnection
con.instanceFollowRedirects = false
con.connectTimeout = 5000
val url: String = con.getHeaderField("Location")
con.disconnect()
onConnectionCallBack.onBack(url)
}
}
interface OnConnectionCallBack {
fun onBack(string: String)
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if (keyCode === KeyEvent.KEYCODE_BACK) {
val home = Intent(Intent.ACTION_MAIN)
home.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
home.addCategory(Intent.CATEGORY_HOME)
startActivity(home)
return true
}
return super.onKeyDown(keyCode, event)
}
fun logged(param: String) {
Log.d("MyKotlinLog", param)
}
fun toast(param: String){
Toast.makeText(this,param, Toast.LENGTH_SHORT)
}
}
布局界面 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <EditText android:id="@+id/edittext" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:hint="请粘贴抖音分享链接到这里" /> <TextView android:id="@+id/textview" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/edittext" android:layout_marginTop="10dp" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:textColor="#000000" android:textSize="16sp"/> <TextView android:id="@+id/textview3" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/textview" android:layout_marginTop="10dp" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:textIsSelectable="true" android:textColor="#000000" android:textSize="16sp" /> <TextView android:id="@+id/textView4" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:layout_below="@id/textview3" android:layout_marginTop="10dp" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:textSize="14sp" android:text="(点击链接到浏览器下载视频或长按链接复制在浏览器打开)"/> <Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/textView4" android:layout_marginTop="10dp" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:text="获取下载地址"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="下载过程中请不要关闭浏览器" android:layout_alignParentBottom="true" android:textSize="16sp" android:gravity="center_horizontal"/> </RelativeLayout>