/// <summary> /// 非同期で動画をダウンロードする /// </summary> private async Task <IAttemptResult> TryDownloadVideoAsync(IDownloadSettings settings, Action <string> onMessage, IWatchSession session, IDownloadContext context, CancellationToken token) { var videoDownloader = DIFactory.Provider.GetRequiredService <Vdl::IVideoDownloader>(); IAttemptResult result; try { result = await videoDownloader.DownloadVideoAsync(settings, onMessage, context, session, token); } catch (Exception e) { this.logger.Error($"動画のダウンロードに失敗しました。({context.GetLogContent()})", e); return(AttemptResult.Fail($"動画のダウンロードに失敗しました。(詳細:{e.Message})")); } return(result); }
public async Task <IAttemptResult> DownloadAsync(IStreamInfo stream, Action <string> onMessage, IDownloadContext context, int maxParallelDownloadCount, string segmentDirectoryName, CancellationToken token) { //変数の初期化 var taskHandler = new ParallelTasksHandler <IParallelDownloadTask>(maxParallelDownloadCount, false); int completed = context.OriginalSegmentsCount - stream.StreamUrls.Count; int all = context.OriginalSegmentsCount; string resolution = context.ActualVerticalResolution == 0 ? string.Empty : $"({context.ActualVerticalResolution}px)"; var failedInOne = false; var tasks = stream.StreamUrls.Select(url => new ParallelDownloadTask(async(self, _) => { if (token.IsCancellationRequested || failedInOne) { self.SetResult(AttemptResult.Fail()); return; } byte[] data; try { data = await this.DownloadInternalAsync(self.Url, self.Context, token); } catch (Exception e) { self.SetResult(AttemptResult.Fail(null, e)); this._logger.Error($"セグメント(idx:{self.SequenceZero})の取得に失敗しました。({context.GetLogContent()})", e); failedInOne = true; return; } this._logger.Log($"セグメント(idx:{self.SequenceZero})を取得しました。({context.GetLogContent()})"); if (token.IsCancellationRequested) { self.SetResult(AttemptResult.Fail()); return; } try { this._writer.Write(data, self, segmentDirectoryName); } catch (Exception e) { self.SetResult(AttemptResult.Fail(null, e)); this._logger.Error($"セグメント(idx:{self.SequenceZero})の書き込みに失敗しました。({context.GetLogContent()})", e); failedInOne = true; return; } completed++; onMessage($"完了: {completed}/{all}{resolution}"); self.SetResult(AttemptResult.Succeeded()); await Task.Delay(1 * 1000, token); }, context, url.AbsoluteUrl, url.SequenceZero, url.FileName)); foreach (var task in tasks) { taskHandler.AddTaskToQueue(task); } await taskHandler.ProcessTasksAsync(ct : token); foreach (var task in tasks) { if (task.Result is not null && !task.Result.IsSucceeded) { return(task.Result); } } return(AttemptResult.Succeeded()); }
private async Task <byte[]> DownloadInternalAsync(string url, IDownloadContext context, CancellationToken token, int retryAttempt = 0) { var res = await this._http.GetAsync(new Uri(url)); if (!res.IsSuccessStatusCode) { //リトライ回数は3回 if (retryAttempt < 3) { retryAttempt++; await Task.Delay(10 * 1000, token); return(await this.DownloadInternalAsync(url, context, token, retryAttempt)); } else { throw new HttpRequestException($"セグメントファイルの取得に失敗しました。(status: {(int)res.StatusCode}, reason_phrase: {res.ReasonPhrase}, url: {url}, {context.GetLogContent()})"); } } return(await res.Content.ReadAsByteArrayAsync()); }
public async Task <IAttemptResult> DownloadIchibaInfo(IWatchSession session, IDownloadSettings settings, Action <string> onMessage, IDownloadContext context) { if (session.Video is null) { return(new AttemptResult() { Message = "セッション情報のVideoがnullです。" }); } onMessage($"市場APIへのアクセスを開始します。({session.Video.Id})"); var getResult = await this._niconicoIchibaHandler.GetIchibaInfo(session.Video.Id); if (!getResult.IsSucceeded || getResult.Data is null) { onMessage($"市場APIからの情報取得に失敗しました。({session.Video.Id})"); if (getResult.Exception is not null) { this._logger.Error($"市場情報の取得に失敗しました。(詳細:{getResult.Message} ,{context.GetLogContent()})", getResult.Exception); } else { this._logger.Error($"市場情報の取得に失敗しました。(詳細:{getResult.Message} ,{context.GetLogContent()})"); } return(new AttemptResult() { Message = getResult.Message }); } onMessage($"市場APIからの情報取得を取得しました。({session.Video.Id})"); string content; if (settings.IchibaInfoType != IchibaInfoTypeSettings.Html) { try { content = this.GetContent(getResult.Data, settings.IchibaInfoType == IchibaInfoTypeSettings.Xml); } catch (Exception e) { this._logger.Error($"市場情報のシリアル化に失敗しました。(詳細:{getResult.Message} ,{context.GetLogContent()})", e); onMessage($"市場情報のシリアル化に失敗しました。({session.Video.Id})"); return(new AttemptResult() { Message = "市場情報のシリアル化に失敗しました。" }); } } else { try { content = this.GetHtmlContent(getResult.Data, session.Video.Id); } catch (Exception e) { this._logger.Error($"市場情報のシリアル化に失敗しました。(詳細:{getResult.Message} ,{context.GetLogContent()})", e); onMessage($"市場情報のシリアル化に失敗しました。({session.Video.Id})"); return(new AttemptResult() { Message = "市場情報のシリアル化に失敗しました。" }); } } string filePath = this._path.GetFilePath(settings.FileNameFormat, session.Video !.DmcInfo, settings.IchibaInfoExt, settings.FolderPath, settings.IsReplaceStrictedEnable, settings.Overwrite, settings.IchibaInfoSuffix); try { this._fileIO.Write(filePath, content); } catch (Exception e) { this._logger.Error($"市場情報ファイルの書き込みに失敗しました。({context.GetLogContent()})", e); onMessage($"市場情報ファイルの書き込みに失敗しました。({session.Video.Id})"); return(new AttemptResult() { Message = $"市場情報ファイルの書き込みに失敗しました。(詳細:{e.Message})" }); } onMessage($"市場情報ファイルの保存が完了しました。({session.Video.Id})"); return(new AttemptResult() { IsSucceeded = true }); }
/// <summary> /// コメントをダウンロードする /// </summary> /// <param name="dmcInfo"></param> /// <param name="settings"></param> /// <returns></returns> public async Task <ICommentCollection> DownloadCommentAsync(IDmcInfo dmcInfo, IDownloadSettings settings, Action <string> onMessage, IDownloadContext context, CancellationToken token) { var(dThread, dFork) = this.GetDefaultPosyTarget(dmcInfo); if (dThread == -1 || dFork == -1) { throw new InvalidOperationException("DefaultPostTargetが見つかりません。"); } var comments = CommentCollection.GetInstance(settings.CommentOffset, dThread, dFork, settings.EnableUnsafeCommentHandle, settings.EnableExperimentalCommentSafetySystem); Response::Chat?first = null; int index = 0; long lastNo = 0; do { int count = comments.Count; if (index > 0) { if (settings.CommentFetchWaitSpan > 0) { onMessage($"待機中...({settings.CommentFetchWaitSpan}ms)"); try { await Task.Delay(settings.CommentFetchWaitSpan, token); } catch { } } onMessage($"過去ログをダウンロード中({index + 1}件目・{count}コメ)"); } long?when = count == 0 ? 0 : first?.Date - 1; List <Response::Comment> retlieved = await this.GetCommentsAsync(dmcInfo, settings, when); comments.Add(retlieved); if (index > 0) { this.logger.Log($"過去ログをダウンロードしました。({index}個目, {retlieved.Count}コメント, {context.GetLogContent()})"); } else { this.logger.Log($"現行スレッドをダウンロードしました。({retlieved.Count}コメント, {context.GetLogContent()})"); } first = comments.GetFirstComment(false); if ((first?.No ?? 0) == lastNo) { break; } else { lastNo = first?.No ?? 0; } if (settings.MaxCommentsCount > 0 && comments.Count > settings.MaxCommentsCount) { var rmCount = comments.Count - settings.MaxCommentsCount; comments.RemoveFor(rmCount); break; } if (!settings.DownloadLog) { break; } else if (index == 0) { onMessage("過去ログのダウンロードを開始します。"); } ++index; } while (first?.No > 1 && !token.IsCancellationRequested); if (token.IsCancellationRequested) { throw new TaskCanceledException("コメントのダウンロードがキャンセルされました。"); } onMessage($"コメントの正規化処理を開始します。"); comments.Distinct(); onMessage($"コメントの正規化処理が完了しました。"); this.logger.Log($"コメントのダウンロードが完了しました。({comments.Count}コメント, {context.GetLogContent()})"); return(comments); }
public async Task <IAttemptResult> DownloadComment(IWatchSession session, IDownloadSettings settings, Action <string> onMessage, IDownloadContext context, CancellationToken token) { if (session.Video is null) { return(AttemptResult.Fail("動画情報が未取得です。")); } string filePath = this._pathOrganizer.GetFilePath(settings.FileNameFormat, session.Video !.DmcInfo, ".xml", settings.FolderPath, settings.IsReplaceStrictedEnable, settings.Overwrite); string ownerFileName = this._pathOrganizer.GetFilePath(settings.FileNameFormat, session.Video !.DmcInfo, ".xml", settings.FolderPath, settings.IsReplaceStrictedEnable, settings.Overwrite, settings.OwnerComSuffix); if (token.IsCancellationRequested) { return(this.GetCancelledResult()); } onMessage("コメントのダウンロードを開始します。"); this._logger.Log($"{settings.NiconicoId}のコメントダウンロードを開始します。({context.GetLogContent()})"); ICommentCollection result; try { result = await this._client.DownloadCommentAsync(session.Video !.DmcInfo, settings, onMessage, context, token); } catch (Exception e) { this._logger.Error($"コメントの取得に失敗しました。({context.GetLogContent()})", e); return(AttemptResult.Fail($"コメントの取得に失敗しました。(詳細:{e.Message})")); } onMessage("コメントのダウンロードが完了しました。"); if (token.IsCancellationRequested) { return(this.GetCancelledResult()); } onMessage("コメントの変換処理を開始します。"); IStoreCommentsData data; try { data = this._commentConverter.ConvertToStoreCommentsData(result, settings); } catch (Exception e) { this._logger.Error($"コメントの解析に失敗しました。({context.GetLogContent()})", e); return(AttemptResult.Fail($"コメントの解析に失敗しました。")); } onMessage("コメントの変換処理が完了しました。"); data.FilePath = filePath; data.OwnerFilPath = ownerFileName; if (token.IsCancellationRequested) { return(this.GetCancelledResult()); } onMessage($"コメントの書き込みを開始します。"); try { this._commentStream.Write(data, settings.Overwrite); } catch (Exception e) { this._logger.Error($"コメントの書き込みに失敗しました。({context.GetLogContent()})", e); return(AttemptResult.Fail("コメントの書き込みに失敗しました。")); } onMessage("コメントのダウンロードが完了しました。"); this._logger.Log($"コメントのダウンロードが完了しました。({context.GetLogContent()})"); return(AttemptResult.Succeeded()); }
public async Task <IAttemptResult> DownloadVideoAsync(IDownloadSettings settings, Action <string> onMessage, IDownloadContext context, IWatchSession session, CancellationToken token) { IAttemptResult OnCanceled() { this._logger.Log($"ユーザーの操作によって動画のダウンロード処理がキャンセルされました。({context.GetLogContent()})"); onMessage("ダウンロードをキャンセル"); return(AttemptResult.Fail("処理がキャンセルされました")); } this.context = context; this._logger.Log($"動画のダウンロードを開始しました。({context.GetLogContent()})"); if (session.IsSessionExipired) { this._logger.Log($"セッションが失効していたため動画のダウンロードをキャンセルします。({context.GetLogContent()})"); return(AttemptResult <string> .Fail("セッションが失効済のためダウンロード出来ません。")); } if (!session.IsSessionEnsured) { if (token.IsCancellationRequested) { return(OnCanceled()); } IAttemptResult sessionR = await this.EnsureSessionAsync(session, settings); if (!sessionR.IsSucceeded) { onMessage(sessionR.Message ?? ""); return(sessionR); } } //ファイル名取得 string fileName = this.GetFileName(session, settings); context.FileName = fileName; if (token.IsCancellationRequested) { return(OnCanceled()); } //ストリーム系 IAttemptResult <IStreamInfo> streamResult = await this.GetStream(session, settings.VerticalResolution); if (!streamResult.IsSucceeded || streamResult.Data is null) { return(AttemptResult.Fail(streamResult.Message, streamResult.Exception)); } IStreamInfo targetStream = streamResult.Data; List <string> rawsegmentFilePaths = targetStream.StreamUrls.Select(u => u.FileName).ToList(); context.ActualVerticalResolution = targetStream.Resolution?.Vertical ?? 0; context.OriginalSegmentsCount = targetStream.StreamUrls.Count; if (token.IsCancellationRequested) { return(OnCanceled()); } //レジューム系 string segmentDirectoryName; IAttemptResult <string> sResult = settings.ResumeEnable ? this.GetAndSetSegmentsDirectoryInfoIfExists(settings.NiconicoId, targetStream.Resolution?.Vertical ?? 0, targetStream) : AttemptResult <string> .Fail(); if (sResult.IsSucceeded && sResult.Data is not null) { segmentDirectoryName = sResult.Data; onMessage("DLをレジューム"); this._logger.Log($"ダウンロードをレジュームします。({context.GetLogContent()})"); } else { segmentDirectoryName = $"{settings.NiconicoId}-{targetStream.Resolution?.Vertical ?? 0}-{DateTime.Now.ToString("yyyy-MM-dd")}"; } List <string> segmentFilePaths = rawsegmentFilePaths.Select(p => Path.Combine(AppContext.BaseDirectory, "tmp", segmentDirectoryName, p)).ToList(); //DL系 IAttemptResult downloadresult = await this.DownloadVideoInternalAsync(targetStream, onMessage, context, settings.MaxParallelSegmentDLCount, segmentDirectoryName, token); if (!downloadresult.IsSucceeded) { return(downloadresult); } if (token.IsCancellationRequested) { return(OnCanceled()); } onMessage("動画を変換中..."); IAttemptResult encodeResult = await this.EncodeVideosAsync(session, context, settings, onMessage, segmentFilePaths, token); if (!encodeResult.IsSucceeded) { return(encodeResult); } onMessage("動画の変換が完了"); this.DeleteTmpFolder(segmentDirectoryName, onMessage); bool isEconomy = session.Video !.DmcInfo.IsEnonomy; if (settings.IsEconomy && settings.DeleteExistingEconomyFile && !isEconomy) { this.RemoveEconomyFile(settings.FilePath); } this._fileStorehandler.Add(settings.NiconicoId, Path.Combine(settings.FolderPath, fileName)); this._logger.Log($"動画のダウンロードが完了しました。({context.GetLogContent()})"); return(AttemptResult.Succeeded()); }