mirror of
https://onedev.site.tesses.net/tytd2025
synced 2026-06-01 09:55:34 +00:00
Make the downloader more resiliant
This commit is contained in:
@@ -14,6 +14,6 @@
|
|||||||
"project_dependencies": [
|
"project_dependencies": [
|
||||||
"..\/Tesses.YouTubeDownloader"
|
"..\/Tesses.YouTubeDownloader"
|
||||||
],
|
],
|
||||||
"version": "1.0.0.2-prod",
|
"version": "1.0.0.3-prod",
|
||||||
"compTime": "secure"
|
"compTime": "secure"
|
||||||
}
|
}
|
||||||
@@ -623,6 +623,17 @@ class TYTDApp {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(ctx.Path == "/api/v1/manifest.json")
|
||||||
|
{
|
||||||
|
const v = ctx.QueryParams.TryGetFirst("v");
|
||||||
|
if(TypeIsString(v))
|
||||||
|
{
|
||||||
|
const resp=this.TYTD.ManifestRequest(v);
|
||||||
|
Console.WriteLine(resp);
|
||||||
|
ctx.WithMimeType("application/json").SendJson(resp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if(ctx.Path == "/api/v1/personal")
|
else if(ctx.Path == "/api/v1/personal")
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -14,6 +14,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"name": "Tesses.YouTubeDownloader",
|
"name": "Tesses.YouTubeDownloader",
|
||||||
"version": "1.0.0.2-prod",
|
"version": "1.0.0.3-prod",
|
||||||
"icon": "icon.png"
|
"icon": "icon.png"
|
||||||
}
|
}
|
||||||
@@ -1341,11 +1341,51 @@ class TYTD.Downloader {
|
|||||||
if(e == null)
|
if(e == null)
|
||||||
{
|
{
|
||||||
var req = this.ManifestRequest(id);
|
var req = this.ManifestRequest(id);
|
||||||
|
this.DownloadCaptions(req);
|
||||||
this.PutVideoInfo(req.playerResponse.videoDetails);
|
this.PutVideoInfo(req.playerResponse.videoDetails);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
private DownloadCaptions(req)
|
||||||
|
{
|
||||||
|
const tracks = req.playerResponse.playerCaptionsTracklistRenderer.captionTracks;
|
||||||
|
if(TypeIsList(tracks))
|
||||||
|
{
|
||||||
|
each(var item : tracks)
|
||||||
|
{
|
||||||
|
if(!TypeIsString(item.languageCode)) continue;
|
||||||
|
if(!TypeIsString(item.baseUrl)) continue;
|
||||||
|
try {
|
||||||
|
var path = /"Streams"/id.Substring(0,4) / id.Substring(4) / item.languageCode;
|
||||||
|
var resp = Net.Http.MakeRequest(url,{FollowRedirects=true});
|
||||||
|
if(resp.StatusCode >= 200 && resp.StatusCode <= 299)
|
||||||
|
{
|
||||||
|
const strm=this.Storage.OpenFile(path+".xml","wb");
|
||||||
|
resp.CopyToStream(strm);
|
||||||
|
strm.Close();
|
||||||
|
}
|
||||||
|
resp = Net.Http.MakeRequest(url.Replace("fmt=srv3","fmt=vtt"),{FollowRedirects=true});
|
||||||
|
if(resp.StatusCode >= 200 && resp.StatusCode <= 299)
|
||||||
|
{
|
||||||
|
const strm=this.Storage.OpenFile(path+".vtt","wb");
|
||||||
|
resp.CopyToStream(strm);
|
||||||
|
strm.Close();
|
||||||
|
}
|
||||||
|
resp = Net.Http.MakeRequest(url.Replace("fmt=srv3","fmt=srt"),{FollowRedirects=true});
|
||||||
|
if(resp.StatusCode >= 200 && resp.StatusCode <= 299)
|
||||||
|
{
|
||||||
|
const strm=this.Storage.OpenFile(path+".srt","wb");
|
||||||
|
resp.CopyToStream(strm);
|
||||||
|
strm.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch(ex) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
/^
|
/^
|
||||||
Put video info from info into database
|
Put video info from info into database
|
||||||
^/
|
^/
|
||||||
@@ -1608,6 +1648,7 @@ class TYTD.Downloader {
|
|||||||
^/
|
^/
|
||||||
public ManifestRequest(vid)
|
public ManifestRequest(vid)
|
||||||
{
|
{
|
||||||
|
while(true) {
|
||||||
var id = TYTD.GetVideoId(vid);
|
var id = TYTD.GetVideoId(vid);
|
||||||
if(id == null) return null;
|
if(id == null) return null;
|
||||||
TryDownloadVideoThumbnail(id,"0");
|
TryDownloadVideoThumbnail(id,"0");
|
||||||
@@ -1651,9 +1692,35 @@ class TYTD.Downloader {
|
|||||||
};
|
};
|
||||||
this.RateLimit();
|
this.RateLimit();
|
||||||
var response = Net.Http.MakeRequest(url,requestData);
|
var response = Net.Http.MakeRequest(url,requestData);
|
||||||
if(response.StatusCode < 200 || response.StatusCode > 299) return null;
|
Console.WriteLine(response.StatusCode);
|
||||||
return Json.Decode(response.ReadAsString());
|
|
||||||
|
if(response.StatusCode < 200 || response.StatusCode > 299) continue;
|
||||||
|
const respText = response.ReadAsString();
|
||||||
|
const jsonResp = Json.Decode(respText);
|
||||||
|
if(!TypeIsDictionary(jsonResp.playerResponse)) continue;
|
||||||
|
|
||||||
|
if(TypeIsDictionary(jsonResp.playerResponse.playabilityStatus))
|
||||||
|
{
|
||||||
|
if(jsonResp.playerResponse.playabilityStatus.status == "ERROR")
|
||||||
|
{
|
||||||
|
throw new VideoDownloadError(id, jsonResp.playerResponse.playabilityStatus.reason);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new VideoDownloadError(id, "playabilityStatus is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!TypeIsDictionary(jsonResp.playerResponse.videoDetails))
|
||||||
|
{
|
||||||
|
throw new VideoDownloadError(id, "videoDetails is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!TypeIsList(jsonResp.playerResponse.streamingData.adaptiveFormats))
|
||||||
|
{
|
||||||
|
throw new VideoDownloadError(id, "adaptiveFormats is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonResp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
16
Tesses.YouTubeDownloader/src/error.tcross
Normal file
16
Tesses.YouTubeDownloader/src/error.tcross
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
class VideoDownloadError {
|
||||||
|
public VideoDownloadError(id, error)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Id;
|
||||||
|
|
||||||
|
public Error;
|
||||||
|
|
||||||
|
public ToString()
|
||||||
|
{
|
||||||
|
return $"Download error {Error} with Id: {Id}";
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user