private IEnumerator DownloadVideoCoroutine(VideoConfig video, VideoQuality.Mode quality) { Log.Info($"Starting download of {video.title}"); _downloadLog = ""; var downloadProcess = CreateDownloadProcess(video, quality); if (downloadProcess == null) { yield break; } video.DownloadState = DownloadState.Downloading; DownloadProgress?.Invoke(video); Log.Info( $"youtube-dl command: \"{downloadProcess.StartInfo.FileName}\" {downloadProcess.StartInfo.Arguments}"); var timeout = new Timeout(5 * 60); downloadProcess.OutputDataReceived += (sender, e) => UnityMainThreadTaskScheduler.Factory.StartNew(delegate { DownloadOutputDataReceived(e, video); }); downloadProcess.ErrorDataReceived += (sender, e) => UnityMainThreadTaskScheduler.Factory.StartNew(delegate { DownloadErrorDataReceived(e); }); downloadProcess.Exited += (sender, e) => UnityMainThreadTaskScheduler.Factory.StartNew(delegate { DownloadProcessExited((Process)sender, video); }); downloadProcess.Disposed += (sender, e) => UnityMainThreadTaskScheduler.Factory.StartNew(delegate { DownloadProcessDisposed((Process)sender, e); }); StartProcessThreaded(downloadProcess); var startProcessTimeout = new Timeout(10); yield return(new WaitUntil(() => IsProcessRunning(downloadProcess) || startProcessTimeout.HasTimedOut)); startProcessTimeout.Stop(); yield return(new WaitUntil(() => !IsProcessRunning(downloadProcess) || timeout.HasTimedOut)); if (timeout.HasTimedOut) { Log.Warn("Timeout reached, disposing download process"); } else { //When the download is finished, wait for process to exit instead of immediately killing it yield return(new WaitForSeconds(2f)); } timeout.Stop(); _downloadLog = ""; DisposeProcess(downloadProcess); }
public void StartDownload(VideoConfig video, VideoQuality.Mode quality) { SharedCoroutineStarter.instance.StartCoroutine(DownloadVideoCoroutine(video, quality)); }
private Process?CreateDownloadProcess(VideoConfig video, VideoQuality.Mode quality) { if (video.LevelDir == null) { Log.Error("LevelDir was null during download"); return(null); } var success = _downloadProcesses.TryGetValue(video, out _); if (success) { Log.Warn("Existing process not cleaned up yet. Cancelling download attempt."); return(null); } if (!Directory.Exists(video.LevelDir)) { //Needed for OST videos Directory.CreateDirectory(video.LevelDir); } var videoFileName = Util.ReplaceIllegalFilesystemChars(video.title ?? video.videoID ?? "video"); videoFileName = Util.ShortenFilename(video.LevelDir, videoFileName); video.videoFile = videoFileName + ".mp4"; string videoUrl; if (video.videoUrl != null) { if (UrlInWhitelist(video.videoUrl)) { videoUrl = video.videoUrl; } else { Log.Error($"Video hoster for {video.videoUrl} is not allowed"); return(null); } } else if (video.videoID != null) { videoUrl = $"https://www.youtube.com/watch?v={video.videoID}"; } else { Log.Error("Video config has neither videoID or videoUrl set"); return(null); } var videoFormat = VideoQuality.ToYoutubeDLFormat(video, quality); videoFormat = $" -f \"{videoFormat}\""; var downloadProcessArguments = videoUrl + videoFormat + " --no-cache-dir" + // Don't use temp storage $" -o \"{videoFileName}.%(ext)s\"" + " --no-playlist" + // Don't download playlists, only the first video " --no-part" + // Don't store download in parts, write directly to file " --recode-video mp4" + //Re-encode to mp4 (will be skipped most of the time, since it's already in an mp4 container) " --no-mtime" + //Video last modified will be when it was downloaded, not when it was uploaded to youtube " --socket-timeout 10"; //Retry if no response in 10 seconds Note: Not if download takes more than 10 seconds but if the time between any 2 messages from the server is 10 seconds var process = CreateProcess(downloadProcessArguments, video.LevelDir); _downloadProcesses.TryAdd(video, process); return(process); }