一个tiktok公开下载器的方案

文章目录

需求背景

实现一个可以下载无水印tiktok公开视频的功能

The TikTok URL link looks like this:

https://vm.tiktok.com/ZSJmdax66/

https://v.douyin.com/RKqEJBV

https://www.tiktok.com/@philandmore/video/6805867805452324102

https://m.tiktok.com/v/6805867805452324102.html

方案

1 py爬虫接口返回视频id

2.1 调用 music.ly 获取下载视频地址信息,直接返回

2.2 或者 http请求来下载文件,返回文件流

第三方公开接口

musical.ly接口直接获取浏览器可下载的接口,只支持tiktok链接,不支持doiuyin

GET https://api19-core-c-useast1a.musical.ly/aweme/v1/feed/?aweme_id=7257420363305323794

转流的示例代码

视频链接下载走服务器流量,传文件流给前端

go 复制代码
func (m *FileBiz) DownloadFileV2(ctx *ctrl.Context, fileLink, fileName string) (err error) {

   // 记录下载日志
   record.BusinessLog(record.Debug, "RecordDownloadFileV2", fmt.Sprintf("filePath:%s,wsId:%s,email:%s", fileLink, ctx.GetString("ws_id"), ctx.GetString("email")), "")

   // 获取地址异常
   if fileLink == "" {
   	err = errInfo.ErrFilesNull
   	return
   }

   // 初始化
   request, err := http.NewRequest("GET", fileLink, nil)
   if err != nil {
   	record.BusinessLog(record.Error, "NewRequest", fmt.Sprintf("filePath:%s", fileLink), err.Error())
   	err = errInfo.ErrHttpInit
   	return
   }

   // 执行请求
   clt := http.Client{}
   resp, err := clt.Do(request)
   if err != nil {
   	record.BusinessLog(record.Error, "HttpDp", fmt.Sprintf("filePath:%s", fileLink), err.Error())
   	err = errInfo.ErrHttpDo
   	return
   }

   defer func(Body io.ReadCloser) {
   	errClose := Body.Close()
   	if errClose != nil {
   		record.BusinessLog(record.Error, "FileClose", fmt.Sprintf("filePath:%s", fileLink), errClose.Error())
   	}
   }(resp.Body)

   // 响应头
   ctx.Header("Content-Length", resp.Header.Get("Content-Length"))
   ctx.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", fileName))
   ctx.Header("Content-Type", "application/octet-stream;charset=UTF-8")
   ctx.Header("Set-Cookie", "download=success; Domain=.media.io; Path=/;")

   // 响应流
   written, err := io.Copy(ctx.ResponseWriter(), resp.Body)
   if err != nil {
   	record.BusinessLog(record.Error, "IoCopy", fmt.Sprintf("filePath:%s, written:%d", fileLink, written), err.Error())
   	err = errInfo.ErrResponseWritten
   }

   return
}

下载地址的密钥校验

为了实现这个需求,你需要在前端和后端之间建立一个认证机制。这里是一个简单的实现方法:

  1. 前端和后端约定一个密钥(secret key),并确保只有它们知道这个密钥。
  2. 前端在发起请求时生成一个有时效性的key(例如,使用Unix时间戳),并将其与密钥一起使用某种方式(例如,HMAC)进行加密,生成一个签名。
  3. 前端将生成的key和签名一起作为请求头发送给后端。
  4. 后端收到请求后,从请求头中获取key和签名,然后使用相同的加密方法对key和密钥进行加密,与请求头中的签名进行比较。
  5. 如果签名匹配,检查key是否过期,如果未过期,则接受请求,否则拒绝请求。如果签名不匹配或请求头中的refer与期望的不匹配,拒绝请求。

结果:最后没有使用前端生成的code,还是后端传code,在单独的服务器上做验证和转流,避免影响主服务

PS:永远不要相信前端传的数据,尽量数据校验都放在后端

以下是一个简单的Go实现示例:

go 复制代码
package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"net/http"
	"strconv"
	"time"
)

const secretKey = "your-secret-key"
const keyExpiration = 60 // Key expiration time in seconds

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		key := r.Header.Get("X-Key")
		signature := r.Header.Get("X-Signature")
		referer := r.Header.Get("Referer")

		// Check referer
		if referer != "your-expected-referer" {
			http.Error(w, "Forbidden", http.StatusForbidden)
			return
		}

		// Check key and signature
		if isValidKey(key, signature) {
			fmt.Fprintln(w, "Welcome!")
		} else {
			http.Error(w, "Forbidden", http.StatusForbidden)
		}
	})

	http.ListenAndServe(":8080", nil)
}

func isValidKey(key, signature string) bool {
	// Check key expiration
	keyTime, err := strconv.ParseInt(key, 10, 64)
	if err != nil {
		return false
	}
	if time.Now().Unix()-keyTime > keyExpiration {
		return false
	}

	// Check signature
	mac := hmac.New(sha256.New, []byte(secretKey))
	mac.Write([]byte(key))
	expectedSignature := hex.EncodeToString(mac.Sum(nil))

	return hmac.Equal([]byte(signature), []byte(expectedSignature))
}

这个示例中,后端服务器会检查请求头中的Referer(请求的来源)、有时效性的X-Key和X-Signature。请注意,这只是一个简单的示例,实际应用中可能需要对加密算法和过期时间进行调整。

相关推荐
microrain8 小时前
读数是 1008,用电量是 8:SagooIoT 计算服务的增量口径与七种计算策略
物联网·golang·计算服务·sagooiot
看浪的路人10 小时前
第7讲:大模型后端服务化——Go 实现高并发 LLM Proxy / Router / Fallback
开发语言·后端·golang
灯澜忆梦12 小时前
【Go Zap 日志库】#1 |快速上手 + 配置详解
开发语言·后端·golang
不是万能的豆学长15 小时前
排查Go服务40天后的内存泄漏问题
开发语言·后端·golang·go
tachibana215 小时前
Go 语言 Map 的底层实现原理
开发语言·后端·golang·make
tachibana216 小时前
Go语言里slice是怎么扩容的?
后端·golang·go·切片
西瓜很甜哟17 小时前
golang实现大文件分片上传
后端·golang·html
tachibana21 天前
使用for range 的时候,它的地址会发生变化吗?
开发语言·后端·golang·go·语法
王中阳Go1 天前
Go 后端转 AI:非科班怎么补?一张 3 列路线表(可保存)
开发语言·人工智能·后端·python·golang·大语言模型
for_ever_love__2 天前
LangChain Agentic RAG:让 Agent 自己决定要不要检索、检索几次
人工智能·golang·langchain·rag·langgraph·agentic·自适应检索