private async Task <IAttemptResult> EncodeVideosAsync(IWatchSession session, IDownloadContext context, IDownloadSettings settings, Action <string> onMessage, IEnumerable <string> segmentFilePaths, CancellationToken token)
        {
            var encodeSetting = new EncodeSettings()
            {
                FilePath           = context.FileName,
                CommandFormat      = settings.CommandFormat,
                TsFilePaths        = segmentFilePaths,
                IsOverwriteEnable  = settings.Overwrite,
                IsOverrideDTEnable = settings.OverrideVideoFileDateToUploadedDT,
                UploadedOn         = session.Video !.DmcInfo.UploadedOn,
                IsNoEncodeEnable   = settings.SaveWithoutEncode,
            };

            try
            {
                await this._encorder.EncodeAsync(encodeSetting, onMessage, token);
            }
            catch (Exception e)
            {
                this._logger.Error($"ファイルの変換中にエラーが発生しました。({this.context!.GetLogContent()})", e);
                onMessage("動画の変換中にエラー発生");
                return(AttemptResult.Fail($"ファイルの変換中にエラーが発生しました。(詳細: {e.Message})"));
            }

            return(AttemptResult.Succeeded());
        }
Exemple #2
0
        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());
        }
Exemple #3
0
        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());
        }
        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>
        private async Task <IAttemptResult> DownloadIchibaInfoAsync(IDownloadSettings settings, IWatchSession session, Action <string> onMessage, IDownloadContext context)
        {
            var            iDownloader = DIFactory.Provider.GetRequiredService <IIchibaInfoDownloader>();
            IAttemptResult result;

            try
            {
                result = await iDownloader.DownloadIchibaInfo(session, settings, onMessage, context);
            }
            catch (Exception e)
            {
                this.logger.Error("市場情報のダウンロードに失敗しました。", e);
                return(AttemptResult.Fail($"市場情報のダウンロードに失敗しました。({e.Message})"));
            }
            return(result);
        }
        /// <summary>
        /// コメントをダウンロードする
        /// </summary>
        private async Task <IAttemptResult> TryDownloadCommentAsync(IDownloadSettings settings, IWatchSession session, Action <string> onMessage, IDownloadContext context, CancellationToken token)
        {
            Cdl::ICommentDownloader?      v1 = null;
            V2Comment::ICommentDownloader?v2 = null;

            if (settings.EnableExperimentalCommentSafetySystem)
            {
                v2 = DIFactory.Provider.GetRequiredService <V2Comment::ICommentDownloader>();
            }
            else
            {
                v1 = DIFactory.Provider.GetRequiredService <Cdl::ICommentDownloader>();
            }

            IAttemptResult result;

            try
            {
                result = v2 is null ? await v1 !.DownloadComment(session, settings, onMessage, context, token) : await v2.DownloadCommentAsync(session.Video !.DmcInfo, settings, context, token);
            }
            catch (Exception e)
            {
                this.logger.Error("コメントのダウンロードに失敗しました。", e);
                return(AttemptResult.Fail($"コメントのダウンロードに失敗しました。({e.Message})"));
            }

            return(result);
        }
        /// <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);
        }
Exemple #8
0
        /// <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);
        }
Exemple #9
0
 public ParallelDownloadTask(Func <IParallelDownloadTask, object, Task> taskFunc, IDownloadContext context, string url, int sequenceZero, string filename)
 {
     this.TaskFunction = taskFunc;
     this.OnWait      += (_) => { };
     this.Context      = context;
     this.Url          = url;
     this.SequenceZero = sequenceZero;
     this.FileName     = filename;
 }
Exemple #10
0
        public async Task <IAttemptResult> DownloadCommentAsync(IDmcInfo dmcInfo, IDownloadSettings settings, IDownloadContext context, CancellationToken token)
        {
            //コメント追記処理
            var origination          = DateTime.Now;
            var originationSpecidied = false;
            var oldComments          = new List <Core::IComment>();

            if (settings.AppendingToLocalComment && this._commentLoader.CommentExists(settings.FolderPath, settings.NiconicoId))
            {
                IAttemptResult <Local::LocalCommentInfo> localResult = this._commentLoader.LoadComment(settings.FolderPath, settings.NiconicoId);
                if (localResult.IsSucceeded && localResult.Data is not null)
                {
                    origination          = localResult.Data.LastUpdatedTime;
                    originationSpecidied = true;
                    oldComments.AddRange(localResult.Data.Comments);
                }
            }

            //キャンセル処理
            token.ThrowIfCancellationRequested();

            //コメント取得処理
            var dlOption = new Fetch::CommentClientOption(originationSpecidied, origination);
            IAttemptResult <(Core::ICommentCollection, Core::IThreadInfo)> dlResult = await this._commentClient.DownloadCommentAsync(dmcInfo, settings, dlOption, context, token);

            if (!dlResult.IsSucceeded)
            {
                return(AttemptResult.Fail(dlResult.Message));
            }

            //キャンセル処理
            token.ThrowIfCancellationRequested();

            //コメント統合処理
            var(collection, threadInfo) = dlResult.Data;
            if (oldComments.Count > 0)
            {
                foreach (var comment in oldComments)
                {
                    collection.Add(comment);
                }
            }

            //コメント書き込み処理
            string path         = this._path.GetFilePath(settings.FileNameFormat, dmcInfo, ".xml", settings.FolderPath, settings.IsReplaceStrictedEnable, settings.Overwrite);
            var    writerOption = new Local::CommentWriterOption(path, settings.OmittingXmlDeclaration, dmcInfo.Id);

            IAttemptResult writeResult = this._commentWriter.WriteComment(collection.Comments, threadInfo, writerOption);

            if (!writeResult.IsSucceeded)
            {
                return(writeResult);
            }

            //キャンセル処理
            token.ThrowIfCancellationRequested();

            //コメ数記録
            context.CommentCount = collection.Count;

            return(AttemptResult.Succeeded());
        }
        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());
        }
Exemple #12
0
        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());
        }
Exemple #13
0
        private async Task <IAttemptResult> DownloadVideoInternalAsync(IStreamInfo targetStream, Action <string> onMessage, IDownloadContext context, int maxParallelSegmentDLCount, string segmentDirectoryName, CancellationToken token)
        {
            try
            {
                await this._videoDownloadHelper.DownloadAsync(targetStream, onMessage, context, maxParallelSegmentDLCount, segmentDirectoryName, token);
            }
            catch (Exception e)
            {
                this._logger.Error($"動画のダウンロード中にエラーが発生しました。({this.context!.GetLogContent()})", e);
                onMessage("動画のダウンロードに失敗");
                return(AttemptResult.Fail($"動画のダウンロード中にエラーが発生しました。(詳細: {e.Message})"));
            }

            return(AttemptResult.Succeeded());
        }