当前位置:   article > 正文

delphi 通过TNetHTTPClient解析抖音无水印高清视频原理及解决X-Bogus签名验证2023-5-1_抖音x-bogus

抖音x-bogus


一、杂谈
        最近有很多热心网友反馈抖音去水印又不行了,之前是时不时被blocked,现在直接连内容都没有了,返回直接就是空了,我们今天简要给大家分析一下请求过程,附上delphi 源码,及生成签名验证,成功请求到json数据的解决方法。


二、请求过程分析
我们还是先获取一个抖音链接

https://v.douyin.com/A2VSVxc/
通过访问重定向

https://www.douyin.com/video/7065264218437717285
然后提取到其中的视频ID

7065264218437717285
如果是之前,我们会直接GET请求

https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=7065264218437717285
然后就能得到响应内容了。

但是这种方法已经失效了,今天我们会讲解如何在增加一些请求头参数以及X-Bogus后,可以仍然获取到JSON格式的数据。如:
{"aweme_detail":{"anchors":null,"authentication_token":"......
.........}
可以看到,获取到的aweme_detail json数据和以前一样。


三、URL参数X-Bogus
X-Bogus你可以理解为是一个根据视频ID及user-agent通过JS生成的用户信息参数,它可以用于校验。
详细的一篇分析可以参考Freebuf上的《【JS 逆向百例】某音 X-Bogus 逆向分析,JSVMP 纯算法还原》。


下面是完整的delphi 源码解析类,主要流程如下:
1.传入抖音分享链接:
https://v.douyin.com/A2VSVxc/
重定向得到:
https://www.douyin.com/video/7065264218437717285

2.提取到其中的视频ID:
7065264218437717285

3.无水印视频接口不变:
https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=7065264218437717285

4.(增加步骤4)根据X-Bogus 算法,传入url链接及USER_AGENT数据,生成一个形如:
https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=7065264218437717285&X-Bogus=DFSzswSL2MtANHxFtG3DB09WcBjv

一个携带X-Bogus签名验证字段的请求链接。使用这个链接发送GET请求,就能得到aweme_detail 的json 数据了。不信大家可以试试。不过,这个链接是不能
在浏览器直接访问的,还必须加上cookie,refer等请求头数据,详情看下面的Tdouyin解析类。

5.关于高清无水印视频链接的获取方法

从"aweme_detail"  json数据解析出视频的Uri项,带入高清视频接口:
https://aweme.snssdk.com/aweme/v1/play/?video_id=v0200fg10000c86doo3c77uai4m711qg&ratio=1080p&line=0


执行重定向getRedirectedUrl()得到高清无水印链接:
https://v95-p-cold.douyinvod.com/9f8215c6204afafffee302e612317776/64201324/video/tos/cn/tos-cn-ve-15c001-alinc2/35721d123b6243cca42398b0c5243c32/?a=1128&ch=0&cr=0&dr=0&cd=0%7C0%7C0%7C0&cv=1&br=2209&bt=2209&cs=0&ds=4&ft=bvjWJkQQqUsmfd4ZFo0OW_EklpPiXnlFZMVJEEy8kdbPD-I&mime_type=video_mp4&qs=0&rc=NDU3Omc3aDY8ZGc7OTkzOUBpajQ6N2Q6ZnJnOzMzNGkzM0BiXjE0NTQvXmExNTVeNTU2YSNuLmpmcjQwbDNgLS1kLS9zcw%3D%3D&l=20230326163724C8959375177E24BE6CEE&btag=a8000

详细步骤看以下TDouyin解析类,关键代码处都有注解:

1.解析类:

  1. unit uDouyin;
  2. interface
  3. uses
  4. windows,classes,System.Net.URLClient, System.Net.HttpClient, System.Net.HttpClientComponent,
  5. System.SysUtils,strutils,uLog,System.RegularExpressions,uFuncs,system.JSON,uConfig,
  6. uVideoInfo,uDownVideo;
  7. const
  8. wm_user=$0400;
  9. wm_downfile=wm_user+100+1; //消息参数;
  10. //USER_AGENT标识客户端的类型,这儿是电脑浏览器端。
  11. USER_AGENT:string='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36';
  12. //USER_AGENT标识客户端的类型,这儿是手机APP端。
  13. USER_AGENT_PHONE:string='Mozilla/5.0 (iPhone; CPU iPhone OS 15_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Mobile/15E148 Safari/604.1';
  14. USER_AGENT_PHONE_2:string='TikTok 26.2.0 rv:262018 (iPhone; iOS 14.4.2; en_US) Cronet';
  15. //无水印视频接口,跟以前一样。
  16. DOUYIN_API_URL:string='https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=' ;
  17. DOUYIN_API_URL_2:string='https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=%s&aid=1128&version_name=23.5.0&device_platform=android&os_version=2333' ;
  18. //高清无水印视频接口:
  19. DOUYIN_API_URL_1080 = 'https://aweme.snssdk.com/aweme/v1/play/?video_id=%s&ratio=1080p&line=0';
  20. type
  21. TDouyin=class(TThread) //支持多线程下载;
  22. private
  23. FId:cardinal; //线程标识;
  24. Furl:string; //分享的链接;;
  25. FRedirectedUrl:string; //重定向后的链接;
  26. Fvideourl:string; //解析后得到的无水印视频链接;
  27. FvideoId:string; //视频id 如:7065264218437717285
  28. FvideoTitle:string; //视频标题
  29. Fnickname:string; //作者昵称
  30. FcoverUrl:string; //视频封面链接
  31. Fmsg:string; //线程消息
  32. Fsavedir:string; //保存视频文件及封面图片的目录
  33. Furl_1080:string; //高清视频链接
  34. Furi_1080:string; //高清视频uri参数 不懂的+v:metabycf
  35. Fphotos:string;
  36. class var Fcookie: string; //cookie参数 ,可从浏览器获取 静态类成员
  37. class var Fform: HWND; //接收消息的窗体句柄 类成员
  38. procedure SetId(id:cardinal); //设置线程id
  39. procedure SetSaveDir(dir:string); //设置保存目录
  40. class procedure SetForm(const hForm: HWND); static; //设置窗体句柄 静态方法
  41. class procedure SetCookie(const cookie: string); static; //设置cookie 静态方法
  42. protected
  43. procedure Execute; override;
  44. public
  45. constructor Create(id:cardinal;url:string);
  46. destructor Destroy;
  47. property id:cardinal read FId write SetId; //id属性
  48. property url:string read Furl; //分享链接 属性
  49. property msg:string read Fmsg; //线程消息 属性
  50. property videourl:string read Fvideourl; //无水印视频链接 属性
  51. property videoTitle:string read FvideoTitle; //视频标题 属性
  52. property nickname:string read Fnickname; //用户昵称
  53. property RedirectedUrl:string read FRedirectedUrl; //重定向链接 属性
  54. property videoId:string read FvideoId; //视频id 属性 如:7065264218437717285
  55. property coverUrl:string read FcoverUrl; //封面链接 属性
  56. property url_1080:string read Furl_1080; //高清视频链接 属性
  57. property photos:string read Fphotos;
  58. property savedir:string read Fsavedir write setSaveDir; //保存目录 属性
  59. function getRedirectedUrl(url:string):string;overload; //获取重定向链接
  60. function getRedirectedUrl(url,refer,user_agent:string):string;overload; //获取重定向链接
  61. function getVideoId(txt:string):string; //解析出视频id 如:7065264218437717285
  62. function getVideoUrl():string; //解析无水印视频地址,封面链接,视频标题 工作流程方法在这儿:
  63. function parseJson(jo:string):string; //解析aweme_detail json数据
  64. class property form: HWND read Fform write SetForm; //窗体句柄 类属性
  65. class property cookie: string read Fcookie write SetCookie; //cookie 类属性
  66. function getPostResult(data:string):string; //post 请求
  67. function getRequestResult2(apiurl:string;Cookie:string):string; //GET 请求
  68. function getBogusUrl(url:string):string; X-Bogus 算法 不明白的+v:metabycf
  69. end;
  70. implementation
  71. //解析无水印视频地址,封面链接,视频标题 工作流程方法在这儿:
  72. function TDouyin.getVideoUrl():string;
  73. var
  74. apiurl,apiurl2,jo:string;
  75. i:integer;
  76. video:TvideoInfo;
  77. down:TdownVideo;
  78. begin
  79. result:='';
  80. FcoverUrl:='';
  81. FvideoUrl:='';
  82. Fphotos:='';
  83. try
  84. //第一步:执行重定向,从而获取到视频id
  85. //如:https://www.douyin.com/video/7065264218437717285
  86. FRedirectedUrl:=getRedirectedUrl(Furl,Furl,USER_AGENT);
  87. log('FRedirectedUrl='+FRedirectedUrl); //日志记录
  88. if(FRedirectedUrl)='' then exit;
  89. //第二步:分析出视频id,如:7065264218437717285
  90. FvideoId:=getVideoId(FRedirectedUrl);
  91. log('FvideoId='+FvideoId); //日志记录
  92. if(FvideoId)='' then exit;
  93. //apiurl:=DOUYIN_API_URL+FvideoId; //视频接口
  94. apiurl:=format(DOUYIN_API_URL_2,[FvideoId]); //视频接口
  95. //第三步:计算X-Bogus验证,加到视频接口上。得到新的请求链接 多了这一步骤。
  96. //如:https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=7065264218437717285&X-Bogus=DFSzswSL2MtANHxFtG3DB09WcBjv
  97. //不明白的+v:metabycf
  98. apiurl2:=getBogusUrl(apiurl); //具有X-Bogus验证的视频接口 多了这一步骤。
  99. log(apiurl2); //日志记录
  100. if(apiurl2='')then begin log('apiurl2=k');exit;end;
  101. //第四步:发送GET请求,带上cookie,refer参数;到这一步,已经能拿到"aweme_detail" json数据了。
  102. jo:=getRequestResult2(apiurl2,Fcookie);
  103. Fmsg:=jo;
  104. log(jo); //日志记录
  105. if(pos('aweme_detail',jo)<=0)then begin
  106. log('aweme_detail=k');
  107. exit;
  108. end;
  109. if(pos('"aweme_detail":null',jo)>0)then exit;
  110. //第五步: 解析 "aweme_detail" json数据
  111. parseJson(jo);
  112. //第六步: 1解析 图文
  113. if(Fphotos<>'')then
  114. begin
  115. video:=TvideoInfo.Create(Fvideotitle,coverUrl,'',Fphotos);
  116. down:=TdownVideo.Create(Fid,video,Fsavedir,false);
  117. //down.form:=Fform;
  118. //down.cookie:=Fcookie;
  119. //if(DEBUG=true)then downvideo.process else
  120. down.Start;
  121. exit;
  122. end;
  123. //第六步: 2解析 高清视频地址
  124. if(Furi_1080<>'')then //Furi_1080为视频 uri
  125. begin
  126. Furl_1080:=format(DOUYIN_API_URL_1080,[Furi_1080]);
  127. log(Furl_1080); //日志记录
  128. Furl_1080:=getRedirectedUrl(Furl_1080,FRedirectedUrl, USER_AGENT_PHONE); //重定向
  129. log('Furl_1080='+Furl_1080); //日志记录
  130. end;
  131. //第七步: 启动下载线程,下载视频文件和封面图片。
  132. if(Fvideotitle<>'')and(Furl_1080<>'')and(FcoverUrl<>'')then
  133. begin
  134. video:=TvideoInfo.Create(Fvideotitle,coverUrl,Furl_1080,'');
  135. down:=TdownVideo.Create(Fid,video,Fsavedir,false);
  136. //down.form:=Fform;
  137. //down.cookie:=Fcookie;
  138. down.Start;
  139. end;
  140. finally
  141. //第八步: 发送解析完成消息。
  142. Fmsg:='complete';
  143. SendMessage(Fform,wm_downfile,2,integer(self));
  144. end;
  145. end;
  146. //第四步:发送GET请求,带上cookie,refer参数;到这一步,已经能拿到"aweme_detail" json数据了。
  147. function TDouyin.getRequestResult2(apiurl:string;Cookie:string):string;
  148. var
  149. client: TNetHTTPClient;
  150. ss: TStringStream;
  151. s,id:string;
  152. AResponse:IHTTPResponse;
  153. i:integer;
  154. begin
  155. try
  156. client := TNetHTTPClient.Create(nil);
  157. SS := TStringStream.Create('', TEncoding.UTF8);
  158. ss.Clear;
  159. with client do
  160. begin
  161. ConnectionTimeout := 10000; // 10秒
  162. ResponseTimeout := 10000; // 10秒
  163. AcceptCharSet := 'utf-8';
  164. UserAgent := USER_AGENT; //1 USER_AGENT USER_AGENT_PHONE_2
  165. client.AllowCookies:=true;
  166. client.HandleRedirects:=true;
  167. Accept:='application/json'; //'*/*'
  168. client.ContentType:='application/json'; //2
  169. client.AcceptLanguage:='zh-CN';
  170. client.CustomHeaders['Cookie'] := cookie;
  171. client.CustomHeaders['Referer'] := Furl;
  172. try
  173. AResponse:=Get(apiurl, ss);
  174. result:=ss.DataString;
  175. except
  176. on E: Exception do
  177. Log(e.Message);
  178. end;
  179. end;
  180. finally
  181. ss.Free;
  182. client.Free;
  183. end;
  184. end;
  185. //第五步: 解析 "aweme_detail" json数据 :分为视频和图文两类
  186. function TDouyin.parseJson(jo:string):string;
  187. var
  188. json,jroot,jvideo,j1,j2: TJSONObject;
  189. arr,arr1:TJSONARRAY;
  190. uri,aweme_type,photo:string;
  191. i:integer;
  192. begin
  193. result:='';
  194. try
  195. json := TJSONObject.ParseJSONValue(jo) as TJSONObject;
  196. if json = nil then exit;
  197. jroot:=json.GetValue('aweme_detail') as TJSONObject;
  198. FvideoTitle:=trim(jroot.GetValue('desc').Value);
  199. aweme_type:=jroot.GetValue('aweme_type').Value;
  200. if(aweme_type='68')then //图文
  201. begin
  202. arr:=jroot.GetValue('images') as TJSONARRAY;
  203. for I := 0 to arr.Size-1 do
  204. begin
  205. j1:=arr.Get(i) as TJSONObject;
  206. arr1:=j1.GetValue('url_list') as TJSONARRAY;
  207. photo:=arr1.Items[0].Value;
  208. Fphotos:=Fphotos+photo+#13#10;
  209. end;
  210. result:='#100#'+Fphotos+'#'+FcoverUrl+'#'+FvideoTitle;
  211. exit;
  212. end;
  213. jvideo:=jroot.GetValue('video') as TJSONObject;
  214. j1:=jvideo.GetValue('cover') as TJSONObject; //cover origin_cover
  215. arr:=j1.GetValue('url_list') as TJSONARRAY;
  216. FcoverUrl:=arr[0].Value;
  217. j1:=jvideo.GetValue('play_addr') as TJSONObject;
  218. arr:=j1.GetValue('url_list') as TJSONARRAY;
  219. FvideoUrl:=arr[0].Value;
  220. FvideoUrl:=stringreplace(FvideoUrl,'playwm','play',[rfReplaceAll]);
  221. Furi_1080:=j1.GetValue('uri').Value;
  222. result:='#100#'+FvideoUrl+'#'+FcoverUrl+'#'+FvideoTitle;
  223. finally
  224. if json <> nil then json.Free;
  225. end;
  226. end;
  227. //第二步:分析出视频id,如:7065264218437717285
  228. function TDouyin.getVideoId(txt:string):string;
  229. var
  230.  m:TMatch;
  231. i:integer;
  232. begin
  233. result:='';
  234. m := TRegEx.Match(txt,'/video/([^/?]+)/');
  235. if(m.Groups[1].Success=false) or (length(m.Groups[1].Value)<>19)then exit;
  236. result:=m.Groups[1].Value;
  237. end;
  238. //X-Bogus 算法 不明白的+v:metabycf
  239. function TDouyin.getBogusUrl(url:string):string;
  240. var
  241. json:TJSONObject;
  242. data:string;
  243. begin
  244. result:='';
  245. try
  246. json:=TJSONObject.Create;
  247. json.AddPair('url',url);
  248. json.AddPair('user_agent',USER_AGENT);
  249. data:=json.ToString;
  250. log(data);
  251. data:=getPostResult(data);
  252. log(data);
  253. if(data='')then exit;
  254. json:=TJSONObject.ParseJSONValue(data) as TJSONObject;
  255. result:=json.GetValue('param').Value;
  256. finally
  257. json.Free;
  258. end;
  259. end;
  260. //第一步:执行重定向,从而获取到视频id
  261. function TDouyin.getRedirectedUrl(url,refer,user_agent:string):string;
  262. var
  263. client: TNetHTTPClient;
  264. ss: TStringStream;
  265. s,id:string;
  266. AResponse:IHTTPResponse;
  267. i:integer;
  268. begin
  269. try
  270. client := TNetHTTPClient.Create(nil);
  271. SS := TStringStream.Create('', TEncoding.UTF8);
  272. ss.Clear;
  273. with client do
  274. begin
  275. ConnectionTimeout := 2000; // 2秒
  276. ResponseTimeout := 2000; // 10秒
  277. AcceptCharSet := 'utf-8';
  278. UserAgent := user_agent;
  279. client.AllowCookies:=true;
  280. client.HandleRedirects:=false;
  281. Accept:='*/*';
  282. client.CustomHeaders['Referer'] := refer;
  283. try
  284. AResponse:=Get(url, ss);
  285. Log('getRedirectedUrl AResponse='+ss.DataString);
  286. s:=AResponse.HeaderValue['Location'];
  287. if(s='')then exit;
  288. result:=s;
  289. except
  290. on E: Exception do
  291. Log(e.Message);
  292. end;
  293. end;
  294. finally
  295. ss.Free;
  296. client.Free;
  297. end;
  298. end;
  299. constructor TDouyin.Create(id:cardinal;url:string);
  300. begin
  301. //inherited;
  302. //FreeOnTerminate := True;
  303. inherited Create(True);
  304. FId:=id;
  305. Furl:=url; //分享链接
  306. Furi_1080:=''; //视频uri
  307. Fphotos:='';
  308. end;
  309. destructor TDouyin.Destroy;
  310. begin
  311. inherited Destroy;
  312. end;
  313. //工作线程
  314. procedure TDouyin.Execute;
  315. begin
  316. try
  317. getVideoUrl();
  318. finally
  319. end;
  320. end;
  321. //------------------------------------------属性方法-------------------------------------
  322. procedure TDouyin.SetId(Id:cardinal);
  323. begin
  324. FId:=Id;
  325. end;
  326. class procedure TDouyin.SetForm(const hForm: HWND);
  327. begin
  328. Fform:=hForm;
  329. end;
  330. procedure TDouyin.SetSaveDir(dir:string);
  331. begin
  332. Fsavedir:=dir;
  333. end;
  334. class procedure TDouyin.SetCookie(const cookie: string);
  335. begin
  336. Fcookie:=cookie;
  337. end;
  338. end.

2、视频信息

  1. unit uVideoInfo;
  2. interface
  3. type
  4. TVideoInfo=class
  5. private
  6. Ftitle:string; //标题
  7. FcoverUrl:string; //封面地址
  8. FvideoUrl:string; //视频地址
  9. Fphotos:string; //图片地址
  10. procedure SetTitle(title:string);
  11. public
  12. property title:string read Ftitle write Settitle;
  13. property coverUrl:string read FcoverUrl;
  14. property videoUrl:string read FvideoUrl;
  15. property photos:string read Fphotos;
  16. constructor Create(title,coverUrl,videoUrl,photos:string);
  17. end;
  18. implementation
  19. constructor TVideoInfo.Create(title,coverUrl,videoUrl,photos:string);
  20. begin
  21. Ftitle:=title;
  22. FcoverUrl:=coverUrl;
  23. FvideoUrl:=videoUrl;
  24. Fphotos:=photos;
  25. end;
  26. procedure TVideoInfo.Settitle(title:string);
  27. begin
  28. Ftitle:=title;
  29. end;
  30. end.

3、下载

  1. unit uDownVideo;
  2. interface
  3. uses
  4. windows,classes,System.Net.URLClient, System.Net.HttpClient, System.Net.HttpClientComponent,
  5. System.SysUtils,strutils,uLog,System.RegularExpressions,uFuncs,system.JSON,uConfig,
  6. uVideoInfo,WinInet,urlmon,shlobj,ioutils;
  7. const
  8. wm_user=$0400;
  9. wm_downfile=wm_user+100+1;
  10. USER_AGENT:string='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36';
  11. type
  12. TDownVideo=class(TThread)
  13. private
  14. FId:cardinal;
  15. Fvideo:TvideoInfo;
  16. Fsavedir:string;
  17. FvideoFilename:string;
  18. FcoverFilename:string;
  19. FphotoFilename:string; //图片文件名
  20. Fmsg:string;
  21. FSerial:boolean; //是否在文件名前面加入序号;
  22. Fsuccess:boolean;
  23. Frefer:string;
  24. class var Fcookie: string;
  25. procedure SetId(id:cardinal);
  26. procedure SetSavedir(dir:string);
  27. procedure SetRefer(refer:string);
  28. class var Fform: HWND;
  29. class procedure SetForm(const hForm: HWND); static;
  30. class procedure SetCookie(const cookie: string); static;
  31. function DownloadFile(SourceFile, DestFile: string): Boolean;
  32. protected
  33. procedure Execute; override;
  34. public
  35. constructor Create(id:cardinal;video:TvideoInfo;savedir:string;bSerial:boolean);
  36. destructor Destroy;
  37. property id:cardinal read FId write SetId;
  38. property savedir:string read Fsavedir write SetSavedir;
  39. property video:TvideoInfo read Fvideo;
  40. property msg:string read Fmsg;
  41. property videoFilename:string read FvideoFilename;
  42. property coverFilename:string read FcoverFilename;
  43. property photoFilename:string read FphotoFilename;
  44. property serial:boolean read Fserial;
  45. property success:boolean read Fsuccess;
  46. property refer:string read Frefer write SetRefer;
  47. class property cookie: string read Fcookie write SetCookie;
  48. class property form: HWND read Fform write SetForm;
  49. function formatFilename(caption:string):string;
  50. function formatDir(dir:string):string;
  51. function GetValidName(s:string):string;
  52. procedure downloadFileLog(SourceFile, DestFile: string);
  53. procedure process();
  54. end;
  55. implementation
  56. //bSerial为是否给文件名加上序号
  57. constructor TDownVideo.Create(id:cardinal;video:TvideoInfo;savedir:string;bSerial:boolean);
  58. begin
  59. //inherited;
  60. //FreeOnTerminate := True;
  61. inherited Create(True);
  62. FId:=id;
  63. Fvideo:=video;
  64. Fsavedir:=formatdir(savedir);
  65. Fmsg:='';
  66. FvideoFilename:='';
  67. FcoverFilename:='';
  68. FphotoFilename:='';
  69. Fsuccess:=false;
  70. Fserial:=bSerial;
  71. if(not directoryexists(Fsavedir))then
  72. forcedirectories(Fsavedir);
  73. end;
  74. destructor TDownVideo.Destroy;
  75. begin
  76. inherited Destroy;
  77. end;
  78. //下载流程
  79. procedure TDownVideo.process();
  80. var
  81. dir,title,photoUrl,photoname:string;
  82. b:boolean;
  83. photolist:tstrings;
  84. i:integer;
  85. begin
  86. photolist:=nil;
  87. try
  88. title:=formatfilename(trim(video.title));
  89. if(Fvideo.photos='')then //下载视频
  90. begin
  91. if(Fserial=true)then
  92. begin
  93. FcoverFilename:=Fsavedir+'\'+inttostr(Fid)+'.'+title+'.webp';
  94. FvideoFilename:=Fsavedir+'\'+inttostr(Fid)+'.'+title+'.mp4';
  95. end else begin
  96. FcoverFilename:=Fsavedir+'\'+title+'.webp';
  97. FvideoFilename:=Fsavedir+'\'+title+'.mp4';
  98. end;
  99. if(video.coverUrl<>'')then downloadFileLog(Fvideo.coverUrl,FcoverFilename);
  100. if(video.videoUrl<>'')then downloadFileLog(Fvideo.videoUrl,FvideoFilename);
  101. end else begin //下载图片
  102. photolist:=tstringlist.Create;
  103. photolist.Text:=video.photos;
  104. if(Fserial=true)then
  105. dir:=Fsavedir+'\'+inttostr(Fid)+'.'+title
  106. else
  107. dir:=Fsavedir+'\'+title;
  108. forcedirectories(dir);
  109. if(Fvideo.coverUrl<>'')then
  110. begin
  111. FcoverFilename:=dir+'\0.封面 '+title+'.webp';
  112. downloadFileLog(Fvideo.coverUrl,FcoverFilename);
  113. end;
  114. for I := 0 to photolist.Count-1 do
  115. begin
  116. photoUrl:=photolist[i];
  117. if(trim(photoUrl)='')then continue;
  118. photoname:=dir+'\'+inttostr(i+1)+'.'+title+'.webp';
  119. downloadFileLog(photoUrl,photoname);
  120. FphotoFilename:=FphotoFilename+photoname+#13#10;
  121. end;
  122. end;
  123. finally
  124. Fmsg:='complete';
  125. if(photolist<>nil)then photolist.Free;
  126. SendMessage(Fform,wm_downfile,1,integer(self));
  127. end;
  128. end;
  129. //线程中执行下载
  130. procedure TDownVideo.Execute;
  131. begin
  132. process();
  133. end;
  134. function TDownVideo.GetValidName(s:string):string;
  135. var
  136. c:char;
  137. txt:string;
  138. begin
  139. txt:=s;
  140. for c in TPath.GetInvalidFileNameChars() do
  141. begin
  142. txt:=stringreplace(txt,c,'',[rfReplaceAll]);
  143. end;
  144. result:=txt;
  145. end;
  146. //去除文件名中的非法字符
  147. function TDownVideo.formatFilename(caption:string):string;
  148. var
  149. s:string;
  150. begin
  151. s:=caption;
  152. if(length(s)>72)then s:=leftstr(s,72);
  153. result:=GetValidName(s);
  154. end;
  155. //去除路径中的非法字符
  156. function TDownVideo.formatDir(dir:string):string;
  157. var
  158. caption:string;
  159. begin
  160. caption:=trim(extractfilename(dir));
  161. if(length(caption)>72)then caption:=leftstr(caption,72);
  162. caption:=GetValidName(caption);
  163. result:=extractfilepath(dir)+caption;
  164. end;
  165. //下载文件
  166. procedure TDownVideo.downloadFileLog(SourceFile, DestFile: string);
  167. begin
  168. if(fileexists(DestFile))then deletefile(DestFile);
  169. if(Downloadfile(SourceFile,DestFile))then
  170. begin
  171. log('成功:'+DestFile);
  172. Fsuccess:=true;
  173. end else begin
  174. log('失败:'+DestFile+' '+SourceFile);
  175. Fsuccess:=false;
  176. end;
  177. end;
  178. //下载文件
  179. function TDownVideo.DownloadFile(SourceFile, DestFile: string): Boolean;
  180. begin
  181. try
  182. DeleteUrlCacheEntry(pchar(SourceFile));
  183. Result := UrlDownloadToFile(nil, PChar(SourceFile), PChar(DestFile), 0, nil) = 0;
  184. except
  185. Result := False;
  186. end;
  187. end;
  188. //------------------------------------------属性方法-------------------------------------
  189. procedure TDownVideo.SetSavedir(dir:string);
  190. begin
  191. Fsavedir:=dir;
  192. end;
  193. procedure TDownVideo.SetRefer(refer:string);
  194. begin
  195. Frefer:=refer;
  196. end;
  197. class procedure TDownVideo.SetCookie(const cookie: string);
  198. begin
  199. Fcookie:=cookie;
  200. end;
  201. procedure TDownVideo.SetId(Id:cardinal);
  202. begin
  203. FId:=Id;
  204. end;
  205. class procedure TDownVideo.SetForm(const hForm: HWND);
  206. begin
  207. Fform:=hForm;
  208. end;
  209. end.

需要技术支持及成品的+v:metabycf

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/510228
推荐阅读
相关标签
  

闽ICP备14008679号