github.com/qjfoidnh/BaiduPCS-Go@v0.0.0-20231011165705-caa18a3765f3/baidupcs/baidupcs.go (about)

     1  // Package baidupcs BaiduPCS RESTful API 工具包
     2  package baidupcs
     3  
     4  import (
     5  	"errors"
     6  	"net/http"
     7  	"net/http/cookiejar"
     8  	"net/url"
     9  	"strconv"
    10  
    11  	"github.com/qjfoidnh/BaiduPCS-Go/baidupcs/expires/cachemap"
    12  	"github.com/qjfoidnh/BaiduPCS-Go/baidupcs/internal/panhome"
    13  	"github.com/qjfoidnh/BaiduPCS-Go/baidupcs/pcserror"
    14  	"github.com/qjfoidnh/BaiduPCS-Go/pcsverbose"
    15  	"github.com/qjfoidnh/BaiduPCS-Go/requester"
    16  )
    17  
    18  const (
    19  	// OperationGetUK 获取UK
    20  	OperationGetUK = "获取UK"
    21  	// OperationGetBDSToken
    22  	OperationGetBDSToken = "获取bdstoken"
    23  	// OperationGetCursorDiff
    24  	OperationGetCursorDiff = "获取cursor后文件列表diff信息"
    25  	// OperationQuotaInfo 获取当前用户空间配额信息
    26  	OperationQuotaInfo = "获取当前用户空间配额信息"
    27  	// OperationFilesDirectoriesMeta 获取文件/目录的元信息
    28  	OperationFilesDirectoriesMeta = "获取文件/目录的元信息"
    29  	// OperationFilesDirectoriesList 获取目录下的文件列表
    30  	OperationFilesDirectoriesList = "获取目录下的文件列表"
    31  	// OperationSearch 搜索
    32  	OperationSearch = "搜索"
    33  	// OperationRemove 删除文件/目录
    34  	OperationRemove = "删除文件/目录"
    35  	// OperationMkdir 创建目录
    36  	OperationMkdir = "创建目录"
    37  	// OperationRename 重命名文件/目录
    38  	OperationRename = "重命名文件/目录"
    39  	// OperationCopy 拷贝文件/目录
    40  	OperationCopy = "拷贝文件/目录"
    41  	// OperationMove 移动文件/目录
    42  	OperationMove = "移动文件/目录"
    43  	// OperationRapidUpload 秒传文件
    44  	OperationRapidUpload = "秒传文件"
    45  	// OperationUpload 上传单个文件
    46  	OperationUpload = "上传单个文件"
    47  	// OperationUploadTmpFile 分片上传—文件分片及上传
    48  	OperationUploadTmpFile = "分片上传—文件分片及上传"
    49  	// OperationUploadCreateSuperFile 分片上传—合并分片文件
    50  	OperationUploadCreateSuperFile = "分片上传—合并分片文件"
    51  	// OperationUploadPrecreate 分片上传—Precreate
    52  	OperationUploadPrecreate = "分片上传—Precreate"
    53  	// OperationUploadSuperfile2 分片上传—Superfile2
    54  	OperationUploadSuperfile2 = "分片上传—Superfile2"
    55  	// OperationDownloadFile 下载单个文件
    56  	OperationDownloadFile = "下载单个文件"
    57  	// OperationDownloadStreamFile 下载流式文件
    58  	OperationDownloadStreamFile = "下载流式文件"
    59  	// OperationLocateDownload 获取下载链接
    60  	OperationLocateDownload = "获取下载链接"
    61  	// OperationLocatePanAPIDownload 从百度网盘首页获取下载链接
    62  	OperationLocatePanAPIDownload = "获取下载链接2"
    63  	// OperationCloudDlAddTask 添加离线下载任务
    64  	OperationCloudDlAddTask = "添加离线下载任务"
    65  	// OperationCloudDlQueryTask 精确查询离线下载任务
    66  	OperationCloudDlQueryTask = "精确查询离线下载任务"
    67  	// OperationCloudDlListTask 查询离线下载任务列表
    68  	OperationCloudDlListTask = "查询离线下载任务列表"
    69  	// OperationCloudDlCancelTask 取消离线下载任务
    70  	OperationCloudDlCancelTask = "取消离线下载任务"
    71  	// OperationCloudDlDeleteTask 删除离线下载任务
    72  	OperationCloudDlDeleteTask = "删除离线下载任务"
    73  	// OperationCloudDlClearTask 清空离线下载任务记录
    74  	OperationCloudDlClearTask = "清空离线下载任务记录"
    75  	// OperationShareSet 创建分享链接
    76  	OperationShareSet = "创建分享链接"
    77  	// OperationShareCancel 取消分享
    78  	OperationShareCancel = "取消分享"
    79  	// OperationShareList 列出分享列表
    80  	OperationShareList = "列出分享列表"
    81  	// OperationShareSURLInfo 获取分享详细信息
    82  	OperationShareSURLInfo = "获取分享详细信息"
    83  	// OperationShareFileSavetoLocal 分享链接转存到网盘
    84  	OperationShareFileSavetoLocal = "分享链接转存到网盘"
    85  	// OperationRapidLinkSavetoLocal 秒传链接转存到网盘
    86  	OperationRapidLinkSavetoLocal = "秒传链接转存到网盘"
    87  	// OperationRecycleList 列出回收站文件列表
    88  	OperationRecycleList = "列出回收站文件列表"
    89  	// OperationRecycleRestore 还原回收站文件或目录
    90  	OperationRecycleRestore = "还原回收站文件或目录"
    91  	// OperationRecycleDelete 删除回收站文件或目录
    92  	OperationRecycleDelete = "删除回收站文件或目录"
    93  	// OperationRecycleClear 清空回收站
    94  	OperationRecycleClear = "清空回收站"
    95  
    96  	// OperationExportFileInfo 导出文件信息
    97  	OperationExportFileInfo = "导出文件信息"
    98  	// OperationGetRapidUploadInfo 获取文件秒传信息
    99  	OperationGetRapidUploadInfo = "获取文件秒传信息"
   100  	// OperationFixMD5 修复文件md5
   101  	OperationFixMD5 = "修复文件md5"
   102  	// OperrationMatchPathByShellPattern 通配符匹配文件路径
   103  	OperrationMatchPathByShellPattern = "通配符匹配文件路径"
   104  
   105  	// PCSBaiduCom pcs api地址
   106  	PCSBaiduCom = "pcs.baidu.com"
   107  	// PanBaiduCom 网盘首页api地址
   108  	PanBaiduCom = "pan.baidu.com"
   109  	// YunBaiduCom 网盘首页api地址2
   110  	YunBaiduCom = "yun.baidu.com"
   111  	// PanAppID 百度网盘appid
   112  	PanAppID = "250528"
   113  	// NetdiskUA 网盘客户端ua
   114  	NetdiskUA = "netdisk;P2SP;3.0.0.8;netdisk;11.12.3;ANG-AN00;android-android;10.0;JSbridge4.4.0;jointBridge;1.1.0;"
   115  	// DotBaiduCom .baidu.com
   116  	DotBaiduCom = ".baidu.com"
   117  	// PathSeparator 路径分隔符
   118  	PathSeparator = "/"
   119  )
   120  
   121  var (
   122  	baiduPCSVerbose = pcsverbose.New("BAIDUPCS")
   123  
   124  	baiduComURL = &url.URL{
   125  		Scheme: "http",
   126  		Host:   "pan.baidu.com",
   127  	}
   128  
   129  	baiduPcsComURL = &url.URL{
   130  		Scheme: "http",
   131  		Host:   "baidupcs.com",
   132  	}
   133  )
   134  
   135  type (
   136  	// BaiduPCS 百度 PCS API 详情
   137  	BaiduPCS struct {
   138  		appID       int                   // app_id
   139  		isHTTPS     bool                  // 是否启用https
   140  		uid         uint64                // 百度uid
   141  		client      *requester.HTTPClient // http 客户端
   142  		accessToken string                // accessToken
   143  		pcsUA       string
   144  		pcsAddr     string
   145  		panUA       string
   146  		isSetPanUA  bool
   147  		ph          *panhome.PanHome
   148  		cacheOpMap  cachemap.CacheOpMap
   149  	}
   150  
   151  	userInfoJSON struct {
   152  		*pcserror.PanErrorInfo
   153  		Records []struct {
   154  			Uk int64 `json:"uk"`
   155  		} `json:"records"`
   156  	}
   157  
   158  	userVarJSON struct {
   159  		*pcserror.PanErrorInfo
   160  		Result struct {
   161  			BDSToken string `json:"bdstoken"`
   162  		} `json:"result"`
   163  	}
   164  )
   165  
   166  //UpdatePCSCookies 去除重名Cookies, 同名cookeis只保留最新的
   167  func (pcs *BaiduPCS) UpdatePCSCookies(reverse bool) {
   168  	cookies := pcs.GetClient().Jar.Cookies(baiduComURL)
   169  	new_cookies := make([]*http.Cookie, 0)
   170  	usedKeys := make(map[string]string)
   171  	if reverse {
   172  		for i := len(cookies) - 1; i >= 0; i-- {
   173  			cookie := cookies[i]
   174  			_, exists := usedKeys[cookie.Name]
   175  			if !exists {
   176  				new_cookies = append(new_cookies, &http.Cookie{
   177  					Name:   cookie.Name,
   178  					Value:  cookie.Value,
   179  					Domain: DotBaiduCom,
   180  				})
   181  				usedKeys[cookie.Name] = ""
   182  			}
   183  		}
   184  		for i, j := 0, len(new_cookies)-1; i < j; i, j = i+1, j-1 {
   185  			new_cookies[i], new_cookies[j] = new_cookies[j], new_cookies[i] //逆序
   186  		}
   187  	} else {
   188  		for i := 0; i < len(cookies); i++ {
   189  			cookie := cookies[i]
   190  			_, exists := usedKeys[cookie.Name]
   191  			if !exists {
   192  				new_cookies = append(new_cookies, &http.Cookie{
   193  					Name:   cookie.Name,
   194  					Value:  cookie.Value,
   195  					Domain: DotBaiduCom,
   196  				})
   197  				usedKeys[cookie.Name] = ""
   198  			}
   199  		}
   200  	}
   201  	client := pcs.GetClient()
   202  	client.ResetCookiejar()
   203  	// fmt.Println(len(new_cookies))
   204  	// for _, c := range new_cookies {
   205  	// 	fmt.Println(c.Value)
   206  	// }
   207  	client.Jar.SetCookies(baiduComURL, new_cookies)
   208  
   209  }
   210  
   211  // NewPCS 提供app_id, 百度BDUSS, 返回 BaiduPCS 对象
   212  func NewPCS(appID int, bduss string) *BaiduPCS {
   213  	client := requester.NewHTTPClient()
   214  	client.ResetCookiejar()
   215  	client.Jar.SetCookies(baiduComURL, []*http.Cookie{
   216  		&http.Cookie{
   217  			Name:   "BDUSS",
   218  			Value:  bduss,
   219  			Domain: DotBaiduCom,
   220  		},
   221  	})
   222  
   223  	return &BaiduPCS{
   224  		appID:  appID,
   225  		client: client,
   226  	}
   227  }
   228  
   229  // NewPCSWithClient 提供app_id, 自定义客户端, 返回 BaiduPCS 对象
   230  func NewPCSWithClient(appID int, client *requester.HTTPClient) *BaiduPCS {
   231  	pcs := &BaiduPCS{
   232  		appID:  appID,
   233  		client: client,
   234  	}
   235  	return pcs
   236  }
   237  
   238  // NewPCSWithCookieStr 提供app_id, cookie 字符串, 返回 BaiduPCS 对象
   239  func NewPCSWithCookieStr(appID int, cookieStr string) *BaiduPCS {
   240  	pcs := &BaiduPCS{
   241  		appID:  appID,
   242  		client: requester.NewHTTPClient(),
   243  	}
   244  
   245  	cookies := requester.ParseCookieStr(cookieStr)
   246  	for _, cookie := range cookies {
   247  		cookie.Domain = DotBaiduCom
   248  	}
   249  
   250  	jar, _ := cookiejar.New(nil)
   251  	jar.SetCookies(baiduComURL, cookies)
   252  	pcs.client.SetCookiejar(jar)
   253  
   254  	return pcs
   255  }
   256  
   257  func (pcs *BaiduPCS) lazyInit() {
   258  	if pcs.client == nil {
   259  		pcs.client = requester.NewHTTPClient()
   260  	}
   261  	if pcs.ph == nil {
   262  		pcs.ph = panhome.NewPanHome(pcs.client)
   263  	}
   264  	if !pcs.isSetPanUA {
   265  		pcs.panUA = NetdiskUA
   266  	}
   267  }
   268  
   269  // GetClient 获取当前的http client
   270  func (pcs *BaiduPCS) GetClient() *requester.HTTPClient {
   271  	pcs.lazyInit()
   272  	return pcs.client
   273  }
   274  
   275  // GetBDUSS 获取BDUSS
   276  func (pcs *BaiduPCS) GetBDUSS() (bduss string) {
   277  	if pcs.client == nil || pcs.client.Jar == nil {
   278  		return ""
   279  	}
   280  	cookies := pcs.client.Jar.Cookies(baiduComURL)
   281  	for _, cookie := range cookies {
   282  		if cookie.Name == "BDUSS" {
   283  			return cookie.Value
   284  		}
   285  	}
   286  	return ""
   287  }
   288  
   289  // GetBAIDUID 获取BAIDUID
   290  func (pcs *BaiduPCS) GetBAIDUID() (baiduid string) {
   291  	if pcs.client == nil || pcs.client.Jar == nil {
   292  		return ""
   293  	}
   294  	cookies := pcs.client.Jar.Cookies(baiduComURL)
   295  	for _, cookie := range cookies {
   296  		if cookie.Name == "BAIDUID" {
   297  			return cookie.Value
   298  		}
   299  	}
   300  	return ""
   301  }
   302  
   303  // SetAPPID 设置app_id
   304  func (pcs *BaiduPCS) SetAPPID(appID int) {
   305  	pcs.appID = appID
   306  }
   307  
   308  // SetUID 设置百度UID
   309  // 只有locatedownload才需要设置此项
   310  func (pcs *BaiduPCS) SetUID(uid uint64) {
   311  	pcs.uid = uid
   312  }
   313  
   314  // SetaccessToken 设置秒传转存用的accesstoken
   315  func (pcs *BaiduPCS) SetaccessToken(accessToken string) {
   316  	pcs.accessToken = accessToken
   317  }
   318  
   319  // SetStoken 设置stoken
   320  func (pcs *BaiduPCS) SetStoken(stoken string) {
   321  	pcs.lazyInit()
   322  	if pcs.client.Jar == nil {
   323  		pcs.client.ResetCookiejar()
   324  	}
   325  
   326  	pcs.client.Jar.SetCookies(baiduComURL, []*http.Cookie{
   327  		&http.Cookie{
   328  			Name:   "STOKEN",
   329  			Value:  stoken,
   330  			Domain: DotBaiduCom,
   331  		},
   332  	})
   333  }
   334  
   335  // SetSboxtkn 设置sboxtkn
   336  func (pcs *BaiduPCS) SetSboxtkn(sboxtkn string) {
   337  	pcs.lazyInit()
   338  	if pcs.client.Jar == nil {
   339  		pcs.client.ResetCookiejar()
   340  	}
   341  
   342  	pcs.client.Jar.SetCookies(baiduComURL, []*http.Cookie{
   343  		&http.Cookie{
   344  			Name:   "SBOXTKN",
   345  			Value:  sboxtkn,
   346  			Domain: DotBaiduCom,
   347  		},
   348  	})
   349  }
   350  
   351  // SetPCSUserAgent 设置 PCS User-Agent
   352  func (pcs *BaiduPCS) SetPCSUserAgent(ua string) {
   353  	pcs.pcsUA = ua
   354  }
   355  
   356  // SetPCSAddr 设置 PCS 服务器地址
   357  func (pcs *BaiduPCS) SetPCSAddr(addr string) {
   358  	pcs.pcsAddr = addr
   359  }
   360  
   361  // SetPanUserAgent 设置 Pan User-Agent
   362  func (pcs *BaiduPCS) SetPanUserAgent(ua string) {
   363  	pcs.panUA = ua
   364  	pcs.isSetPanUA = true
   365  }
   366  
   367  // SetHTTPS 是否启用https连接
   368  func (pcs *BaiduPCS) SetHTTPS(https bool) {
   369  	pcs.isHTTPS = https
   370  }
   371  
   372  // URL 返回 url
   373  func (pcs *BaiduPCS) URL() *url.URL {
   374  	host := pcs.pcsAddr
   375  	if host == "" {
   376  		host = PCSBaiduCom
   377  	}
   378  	return &url.URL{
   379  		Scheme: GetHTTPScheme(pcs.isHTTPS),
   380  		Host:   host,
   381  	}
   382  }
   383  
   384  func (pcs *BaiduPCS) getPanUAHeader() (header map[string]string) {
   385  	return map[string]string{
   386  		"User-Agent": pcs.panUA,
   387  	}
   388  }
   389  
   390  func (pcs *BaiduPCS) generatePCSURL(subPath, method string, param ...map[string]string) *url.URL {
   391  	pcsURL := pcs.URL()
   392  	pcsURL.Path = "/rest/2.0/pcs/" + subPath
   393  
   394  	uv := pcsURL.Query()
   395  	uv.Set("app_id", strconv.Itoa(pcs.appID))
   396  	uv.Set("method", method)
   397  	for k := range param {
   398  		for k2 := range param[k] {
   399  			uv.Set(k2, param[k][k2])
   400  		}
   401  	}
   402  
   403  	pcsURL.RawQuery = uv.Encode()
   404  	return pcsURL
   405  }
   406  
   407  func (pcs *BaiduPCS) generatePCSURL2(subPath, method string, param ...map[string]string) *url.URL {
   408  	pcsURL2 := &url.URL{
   409  		Scheme: GetHTTPScheme(pcs.isHTTPS),
   410  		Host:   PanBaiduCom,
   411  		Path:   "/rest/2.0/" + subPath,
   412  	}
   413  
   414  	uv := pcsURL2.Query()
   415  	uv.Set("method", method)
   416  	for k := range param {
   417  		for k2 := range param[k] {
   418  			uv.Set(k2, param[k][k2])
   419  		}
   420  	}
   421  
   422  	pcsURL2.RawQuery = uv.Encode()
   423  	return pcsURL2
   424  }
   425  
   426  func (pcs *BaiduPCS) generatePanURL(subPath string, param map[string]string) *url.URL {
   427  	panURL := url.URL{
   428  		Scheme: GetHTTPScheme(pcs.isHTTPS),
   429  		Host:   PanBaiduCom,
   430  		Path:   "/api/" + subPath,
   431  	}
   432  
   433  	if param != nil {
   434  		uv := url.Values{}
   435  		for k := range param {
   436  			uv.Set(k, param[k])
   437  		}
   438  		panURL.RawQuery = uv.Encode()
   439  	}
   440  	return &panURL
   441  }
   442  
   443  // UK 获取用户 UK
   444  func (pcs *BaiduPCS) UK() (uk int64, pcsError pcserror.Error) {
   445  	dataReadCloser, pcsError := pcs.PrepareUK()
   446  	if pcsError != nil {
   447  		return
   448  	}
   449  
   450  	defer dataReadCloser.Close()
   451  
   452  	errInfo := pcserror.NewPanErrorInfo(OperationGetUK)
   453  	jsonData := userInfoJSON{
   454  		PanErrorInfo: errInfo,
   455  	}
   456  
   457  	pcsError = pcserror.HandleJSONParse(OperationGetUK, dataReadCloser, &jsonData)
   458  	if pcsError != nil {
   459  		return
   460  	}
   461  
   462  	if len(jsonData.Records) != 1 {
   463  		errInfo.ErrType = pcserror.ErrTypeOthers
   464  		errInfo.Err = errors.New("Unknown remote data")
   465  		return 0, errInfo
   466  	}
   467  
   468  	return jsonData.Records[0].Uk, nil
   469  }
   470  
   471  func (pcs *BaiduPCS) BDSToken() (bdstoken string, pcsError pcserror.Error) {
   472  	dataReadCloser, pcsError := pcs.PrepareBDStoken()
   473  	if pcsError != nil {
   474  		return
   475  	}
   476  
   477  	defer dataReadCloser.Close()
   478  
   479  	errInfo := pcserror.NewPanErrorInfo(OperationGetBDSToken)
   480  	jsonData := userVarJSON{
   481  		PanErrorInfo: errInfo,
   482  	}
   483  
   484  	pcsError = pcserror.HandleJSONParse(OperationGetBDSToken, dataReadCloser, &jsonData)
   485  	if pcsError != nil {
   486  		return
   487  	}
   488  
   489  	if jsonData.Result.BDSToken == "" {
   490  		errInfo.ErrType = pcserror.ErrTypeOthers
   491  		errInfo.Err = errors.New("Unknown remote data")
   492  		return "", errInfo
   493  	}
   494  
   495  	return jsonData.Result.BDSToken, nil
   496  }