/// <summary>
        /// XML文字列を取得する
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        public string GetXmlContent(IDmcInfo info)
        {
            var data = new Xml::GetThumbInfoApiResponse();

            data.Thumb.VideoId       = info.Id;
            data.Thumb.Title         = info.Title;
            data.Thumb.Description   = info.Description;
            data.Thumb.ThumbnailUrl  = info.ThumbInfo.GetSpecifiedThumbnail(ThumbSize.Large);
            data.Thumb.FirstRetrieve = new DateTimeOffset(info.UploadedOn, TimeSpan.FromHours(9)).ToString("yyyy-MM-ddTHH:mm:sszzz");
            data.Thumb.Length        = $"{Math.Floor((double)info.Duration / 60).ToString().PadLeft(2, '0')}:{info.Duration % 60}";
            data.Thumb.ViewCounter   = info.ViewCount;
            data.Thumb.CommentNum    = info.CommentCount;
            data.Thumb.LikeCounter   = info.LikeCount;
            data.Thumb.MylistCounter = info.MylistCount;
            data.Thumb.WatchUrl      = Const::NetConstant.NiconicoWatchUrl + info.Id;
            data.Thumb.Tags.Tag.AddRange(info.Tags.Select(t => new Xml::Tag()
            {
                Text = t
            }));
            data.Thumb.UserId       = info.ChannelName.IsNullOrEmpty() ? info.OwnerID.ToString() : null;
            data.Thumb.UserNickname = info.ChannelName.IsNullOrEmpty() ? info.Owner : null;
            data.Thumb.ChName       = info.ChannelName.IsNullOrEmpty() ? null : info.ChannelName;
            data.Thumb.ChId         = info.ChannelName.IsNullOrEmpty() ? null : info.ChannelID;

            return(Xmlparser.Serialize(data));
        }
        /// <summary>
        /// 動画情報本文を取得する
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        public string GetContent(IDmcInfo info)
        {
            var ownerNameTitle = info.ChannelName.IsNullOrEmpty() ? "[owner_nickname]" : "[channel_name]";
            var ownerIDTitle   = info.ChannelName.IsNullOrEmpty() ? "[owner_id]" : "[channel_id]";
            var ownerName      = info.ChannelName.IsNullOrEmpty() ? info.Owner : info.ChannelName;
            var ownerID        = info.ChannelName.IsNullOrEmpty() ? info.OwnerID.ToString() : info.ChannelID;

            var list = new List <string>()
            {
                "[name]",
                info.Id + Environment.NewLine,
                "[post]",
                info.UploadedOn.ToString("yyyy/MM/dd HH:mm:ss") + Environment.NewLine,
                "[title]",
                info.Title + Environment.NewLine,
                "[comment]",
                info.Description + Environment.NewLine,
                "[tags]",
                String.Join(Environment.NewLine, info.Tags) + Environment.NewLine,
                "[view_counter]",
                info.ViewCount.ToString() + Environment.NewLine,
                "[comment_num]",
                info.CommentCount.ToString() + Environment.NewLine,
                "[mylist_counter]",
                info.MylistCount.ToString() + Environment.NewLine,
                "[length]",
                $"{Math.Floor((double)(info.Duration/60))}分{info.Duration%60}秒" + Environment.NewLine,
                ownerIDTitle,
                ownerID + Environment.NewLine,
                ownerNameTitle,
                ownerName
            };

            return(string.Join(Environment.NewLine, list));
        }
示例#3
0
        /// <summary>
        /// コメントを取得する
        /// </summary>
        /// <param name="dmcInfo"></param>
        /// <param name="settings"></param>
        /// <param name="when"></param>
        /// <returns></returns>
        private async Task <List <Response::Comment> > GetCommentsAsync(IDmcInfo dmcInfo, IDownloadSettings settings, long?when = null)
        {
            var option = new CommentOptions()
            {
                NoEasyComment = !settings.DownloadEasy,
                OwnerComment  = settings.DownloadOwner,
                When          = when ?? 0
            };
            var request = await this.requestBuilder.GetRequestDataAsync(dmcInfo, option);

            string server = dmcInfo.CommentThreads.FirstOrDefault()?.Server ?? @"https://nmsg.nicovideo.jp/api.json";

            if (!server.EndsWith("/api.json"))
            {
                server += "/api.json";
            }

            var res = await this.http.PostAsync(new Uri(server), new StringContent(request));

            if (!res.IsSuccessStatusCode)
            {
                throw new HttpRequestException($"コメントの取得に失敗しました。(status_code:{(int)res.StatusCode}, reason_phrase:{res.ReasonPhrase})");
            }

            string content = await res.Content.ReadAsStringAsync();

            var data = JsonParser.DeSerialize <List <Response::Comment> >(content);

            return(data);
        }
示例#4
0
        public async Task <IAttemptResult <string> > BuildRequestAsync(IDmcInfo dmcInfo, ICommentFetchOption option, string key)
        {
            IAttemptResult <List <Request::RequestRoot> > result;

            try
            {
                result = await this.BuildRequestAsyncInternal(dmcInfo, option, key);
            }
            catch (Exception ex)
            {
                this._logger.Error("リクエストの構築中にエラーが発生しました。", ex);
                return(AttemptResult <string> .Fail($"リクエストの構築中にエラーが発生しました。(詳細:{ex.Message})"));
            }

            if (!result.IsSucceeded || result.Data is null)
            {
                return(AttemptResult <string> .Fail(result.Message));
            }

            string json;

            try
            {
                json = JsonParser.Serialize(result.Data);
            }
            catch (Exception ex)
            {
                this._logger.Error("リクエストのシリアライズ中にエラーが発生しました。", ex);
                return(AttemptResult <string> .Fail($"リクエストのシリアライズ中にエラーが発生しました。(詳細:{ex.Message})"));
            }

            return(AttemptResult <string> .Succeeded(json));
        }
示例#5
0
        public string GetFilePath(string format, IDmcInfo dmcInfo, string extension, string folderName, bool replaceStricted, bool overWrite, string?suffix = null)
        {
            var filename = this.niconicoUtils.GetFileName(format, dmcInfo, extension, replaceStricted, suffix);
            var filePath = Path.Combine(folderName, filename);

            if (!overWrite)
            {
                filePath = IOUtils.CheclFileExistsAndReturnNewFilename(filePath);
            }

            return(filePath);
        }
示例#6
0
        private (long, long) GetDefaultPosyTarget(IDmcInfo dmcInfo)
        {
            foreach (var thread in dmcInfo.CommentThreads)
            {
                if (thread.IsDefaultPostTarget)
                {
                    return(thread.ID, thread.Fork);
                }
            }

            return(-1, -1);
        }
        /// <summary>
        /// JSON文字列を取得する
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        public string GetJsonContent(IDmcInfo info)
        {
            var data    = new VideoInfoJson(info);
            var options = new JsonSerializerOptions()
            {
                PropertyNamingPolicy        = JsonNamingPolicy.CamelCase,
                PropertyNameCaseInsensitive = false,
                DefaultIgnoreCondition      = JsonIgnoreCondition.WhenWritingNull,
                Encoder       = JavaScriptEncoder.Create(UnicodeRanges.All),
                WriteIndented = true
            };

            return(JsonParser.Serialize(data, options));
        }
示例#8
0
        /// <summary>
        /// セッション情報からファイル名を取得する
        /// </summary>
        /// <param name="format"></param>
        /// <param name="session"></param>
        /// <returns></returns>
        public string GetFileName(string format, IDmcInfo dmcInfo, string extension, bool replaceStricted, string?suffix = null)
        {
            var info = new VideoInfoForPath()
            {
                Title             = dmcInfo.Title,
                OwnerName         = dmcInfo.Owner,
                NiconicoID        = dmcInfo.Id,
                UploadedOn        = dmcInfo.UploadedOn,
                DownloadStartedOn = dmcInfo.DownloadStartedOn,
                OwnerID           = dmcInfo.OwnerID.ToString(),
                Duration          = dmcInfo.Duration,
            };

            return(this.GetFilenameInternal(format, info, extension, replaceStricted, suffix));
        }
 public VideoInfoJson(IDmcInfo info)
 {
     this.Title       = info.Title;
     this.Post        = info.UploadedOn.ToString("yyyy/MM/ddTHH:mm:ss");
     this.ID          = info.Id;
     this.Description = info.Description;
     this.Tags        = new List <string>();
     this.Tags.AddRange(info.Tags);
     this.ViewCount    = info.ViewCount;
     this.MylistCount  = info.MylistCount;
     this.CommentCount = info.CommentCount;
     this.LikeCount    = info.LikeCount;
     this.Owner        = info.ChannelName.IsNullOrEmpty() ? info.Owner : null;
     this.OwnerID      = info.ChannelName.IsNullOrEmpty() ? info.OwnerID.ToString() : null;
     this.ChannelName  = info.ChannelName.IsNullOrEmpty() ? null : info.ChannelName;
     this.ChannelID    = info.ChannelName.IsNullOrEmpty() ? null : info.ChannelID;
 }
示例#10
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);
        }
示例#11
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());
        }
示例#12
0
 public async Task <IAttemptResult <List <Request::RequestRoot> > > BuildRequestAsyncInternalForTest(IDmcInfo dmcInfo, ICommentFetchOption option, string key)
 {
     return(await this.BuildRequestAsyncInternal(dmcInfo, option, key));
 }
示例#13
0
        /// <summary>
        /// ThreadLeaves要求を取得
        /// </summary>
        /// <param name="thread"></param>
        /// <param name="dmcInfo"></param>
        /// <param name="option"></param>
        /// <param name="wayBackKey"></param>
        /// <returns></returns>
        private IAttemptResult <Request::ThreadLeaves> GetThreadLeaves(IThread thread, IDmcInfo dmcInfo, ICommentFetchOption option, string?wayBackKey)
        {
            var leaves = new Request::ThreadLeaves()
            {
                ThreadNo = thread.ID.ToString(),
                UserID   = dmcInfo.UserId,
                Fork     = thread.Fork,
                Language = 0,
                Scores   = 1,
                Nicoru   = 3,
            };

            //force184?
            if (thread.Is184Forced)
            {
                leaves.Force184 = "1";
            }

            //公式動画を判別
            if (thread.IsThreadkeyRequired)
            {
                leaves.ThreadKey = thread.Threadkey;
            }
            else
            {
                leaves.UserKey = dmcInfo.Userkey;
            }

            //過去ログ
            if (option.DownloadLog)
            {
                leaves.When       = option.When;
                leaves.WayBackKey = wayBackKey;
            }

            int flooredDuration;

            try
            {
                flooredDuration = (int)Math.Floor(dmcInfo.Duration / 60f);
            }
            catch (Exception ex)
            {
                this._logger.Error($"動画再生時間の計算に失敗しました。", ex);
                return(AttemptResult <Request::ThreadLeaves> .Fail($"動画再生時間の計算に失敗しました。(詳細:{ex.Message})"));
            }

            //leaves.Content = $"0-{flooredDuration}:100,1000:nicoru:100";
            leaves.Content = "1000";

            return(AttemptResult <Request::ThreadLeaves> .Succeeded(leaves));
        }
示例#14
0
        /// <summary>
        /// Thread要求を構築する
        /// </summary>
        /// <param name="threadInfo"></param>
        /// <param name="dmcInfo"></param>
        /// <param name="option"></param>
        /// <returns></returns>
        private async Task <IAttemptResult <Request::Thread> > GetThreadAsync(IThread threadInfo, IDmcInfo dmcInfo, ICommentFetchOption option)
        {
            var thread = new Request::Thread()
            {
                ThreadNo   = threadInfo.ID.ToString(),
                UserID     = dmcInfo.UserId,
                Fork       = threadInfo.Fork,
                Language   = 0,
                WithGlobal = 1,
                Scores     = 1,
                Nicoru     = 3,
                Force184   = threadInfo.Is184Forced ? "1" : null,
            };

            //投コメを判定
            if (threadInfo.IsOwnerThread)
            {
                thread.ResFrom = -1000;
                thread.Version = "20061206";
            }
            else
            {
                thread.Version = "20090904";
            }

            //Force184?
            if (threadInfo.Is184Forced)
            {
                thread.Force184 = "1";
            }


            //公式動画を判別
            if (threadInfo.IsThreadkeyRequired)
            {
                thread.ThreadKey = threadInfo.Threadkey;
            }
            else
            {
                thread.UserKey = dmcInfo.Userkey;
            }

            //過去ログ
            if (option.DownloadLog)
            {
                IAttemptResult <WayBackKey> wResult = await this._officialCommentHandler.GetWayBackKeyAsync(threadInfo.ID.ToString());

                if (!wResult.IsSucceeded || wResult.Data is null)
                {
                    return(AttemptResult <Request::Thread> .Fail(wResult.Message));
                }

                thread.When       = option.When;
                thread.WayBackKey = wResult.Data.Key;
            }

            return(AttemptResult <Request::Thread> .Succeeded(thread));
        }
示例#15
0
        /// <summary>
        /// 内部的にリクエストを構築
        /// </summary>
        /// <param name="dmcInfo"></param>
        /// <param name="option"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        private async Task <IAttemptResult <List <Request::RequestRoot> > > BuildRequestAsyncInternal(IDmcInfo dmcInfo, ICommentFetchOption option, string key)
        {
            if (!this.CheckKey(key))
            {
                return(AttemptResult <List <Request::RequestRoot> > .Fail("Keyが違います。"));
            }

            var request = new List <Request::RequestRoot>();

            //リクエスト開始Ping要求を挿入
            request.Add(new Request::RequestRoot()
            {
                Ping = this.GetPingContent(PingType.StartRequest, this.requestIndex)
            });

            //ループ
            foreach (IThread thread in dmcInfo.CommentThreads)
            {
                //かんたんコメント判定
                if (!option.DownloadEasy && thread.IsEasyCommentPostTarget)
                {
                    continue;
                }

                //投コメ判定
                if (!option.DownloadOwner && thread.IsOwnerThread)
                {
                    continue;
                }

                //Thread要求を挿入
                IAttemptResult <Request::Thread> tResult = await this.GetThreadAsync(thread, dmcInfo, option);

                if (!tResult.IsSucceeded || tResult.Data is null)
                {
                    return(AttemptResult <List <Request::RequestRoot> > .Fail(tResult.Message));
                }

                request.Add(new Request::RequestRoot()
                {
                    Ping = this.GetPingContent(PingType.StartCommand, this.commandIndex)
                });
                request.Add(new Request::RequestRoot()
                {
                    Thread = tResult.Data
                });
                request.Add(new Request::RequestRoot()
                {
                    Ping = this.GetPingContent(PingType.StartCommand, this.commandIndex)
                });
                this.commandIndex++;

                //ThreadLeaves要求を挿入
                if (thread.IsLeafRequired)
                {
                    IAttemptResult <Request::ThreadLeaves> lResult = this.GetThreadLeaves(thread, dmcInfo, option, tResult.Data.WayBackKey);
                    if (!lResult.IsSucceeded || lResult.Data is null)
                    {
                        return(AttemptResult <List <Request::RequestRoot> > .Fail(lResult.Message));
                    }

                    request.Add(new Request::RequestRoot()
                    {
                        Ping = this.GetPingContent(PingType.StartCommand, this.commandIndex)
                    });
                    request.Add(new Request::RequestRoot()
                    {
                        ThreadLeaves = lResult.Data
                    });
                    request.Add(new Request::RequestRoot()
                    {
                        Ping = this.GetPingContent(PingType.EndCommand, this.commandIndex)
                    });

                    this.commandIndex++;
                }
            }

            //リクエスト終了Ping要求を挿入
            request.Add(new Request::RequestRoot()
            {
                Ping = this.GetPingContent(PingType.EndRequest, this.requestIndex)
            });

            this.requestIndex++;

            return(AttemptResult <List <Request::RequestRoot> > .Succeeded(request));
        }