public Downloader(WebtoonInfo webtoonInfo, Config config)
 {
     agent            = new Agent();
     parser           = new Parser(agent);
     this.webtoonInfo = webtoonInfo;
     fileNameBuilder  = new FileNameBuilder(webtoonInfo, config);
 }
        public static ImageKey[] BuildImageKeysToDown(WebtoonInfo webtoonInfo)
        {
            List <ImageKey> list   = new List <ImageKey>();
            int             latest = webtoonInfo.GetLastEpisodeNo();

            for (int episodeNo = 1; episodeNo <= latest; episodeNo++)
            {
                if (!webtoonInfo.Episodes.ContainsKey(episodeNo))
                {
                    continue;
                }
                EpisodeInfo episodeInfo = webtoonInfo.Episodes[episodeNo];
                string[]    imageUrls   = episodeInfo.EpisodeImageUrls;
                for (int imageIndex = 0; imageIndex < imageUrls.Length; imageIndex++)
                {
                    ImageKey imageKey = new ImageKey(webtoonInfo.WebtoonTitleId, episodeNo, imageIndex);
                    if (IO.Exists(BuildImageFileFullDirectory(webtoonInfo, imageKey), BuildImageFileName(webtoonInfo, imageKey)))
                    {
                        continue;
                    }
                    list.Add(imageKey);
                }
            }
            return(list.ToArray());
        }
        /// <summary>
        /// webtoonKey가 지정하는 웹툰의 폴더 이름을 정의된 포맷 형식(<seealso cref="Config.WebtoonDirectoryNameFormat"/>)에 따라 생성합니다.
        /// </summary>
        /// <param name="episodeKey"></param>
        /// <returns></returns>
        private static string BuildWebtoonDirectoryName(WebtoonInfo webtoonInfo, WebtoonKey webtoonKey)
        {
            string titleId      = webtoonInfo.WebtoonTitleId;
            string webtoonTitle = webtoonInfo.WebtoonTitle;

            return(string.Format(config.WebtoonDirectoryNameFormat,
                                 titleId,
                                 ReplaceFolderName(webtoonTitle)));
        }
        /// <summary>
        /// imageKey가 지정하는 이미지의 전체 경로 문자열을 반환합니다.
        /// </summary>
        /// <param name="imageKey"></param>
        /// <returns></returns>
        private static string BuildImageFileFullDirectory(WebtoonInfo webtoonInfo, ImageKey imageKey)
        {
            string directory =
                config.DefaultDownloadDirectory + "\\" +
                BuildWebtoonDirectoryName(webtoonInfo, imageKey) + "\\" +
                BuildEpisodeDirectoryName(webtoonInfo, imageKey);

            return(directory);
        }
        /// <summary>
        /// episodeKey가 지정하는 회차의 폴더 이름을 정의된 포맷 형식(<seealso cref="Config.EpisodeDirectoryNameFormat"/>)에 따라 생성합니다.
        /// </summary>
        /// <param name="episodeKey"></param>
        /// <returns></returns>
        private static string BuildEpisodeDirectoryName(WebtoonInfo webtoonInfo, EpisodeKey episodeKey)
        {
            string titleId      = episodeKey.TitleId;
            int    episodeNo    = episodeKey.EpisodeNo;
            string date         = webtoonInfo.Episodes[episodeKey.EpisodeNo].EpisodeDate;
            string webtoonTitle = webtoonInfo.WebtoonTitle;
            string episodeTitle = webtoonInfo.Episodes[episodeKey.EpisodeNo].EpisodeTitle;

            return(string.Format(config.EpisodeDirectoryNameFormat,
                                 titleId,
                                 episodeNo,
                                 date,
                                 ReplaceFolderName(webtoonTitle),
                                 ReplaceFolderName(episodeTitle)));
        }
        /// <summary>
        /// imageKey가 지정하는 이미지의 파일명을 정의된 포맷 형식(<seealso cref="Config.ImageFileNameFormat"/>)에 따라 생성합니다.
        /// </summary>
        /// <param name="imageKey"></param>
        /// <returns></returns>
        private static string BuildImageFileName(WebtoonInfo webtoonInfo, ImageKey imageKey)
        {
            string titleId      = imageKey.TitleId;
            int    episodeNo    = imageKey.EpisodeNo;
            int    ImageIndex   = imageKey.ImageIndex;
            string webtoonTitle = webtoonInfo.WebtoonTitle;
            string episoneTitle = webtoonInfo.Episodes[imageKey.EpisodeNo].EpisodeTitle;

            return(string.Format(config.ImageFileNameFormat,
                                 titleId,
                                 episodeNo,
                                 ImageIndex,
                                 ReplaceFileName(webtoonTitle),
                                 ReplaceFileName(episoneTitle),
                                 webtoonInfo.Episodes[imageKey.EpisodeNo].EpisodeDate));
        }
        /// <summary>
        /// "{0}({1}) [{2}/{3}] ({4:P}) [{5}]"
        /// <code>{0}:웹툰 제목</code>
        /// <code>{1}:웹툰 아이디</code>
        /// <code>{2}:현재 포지션</code>
        /// <code>{3}:총 작업수</code>
        /// <code>{4}:퍼센트</code>
        /// <code>{5}:회차 날짜</code>
        /// </summary>
        /// <param name="webtoonInfo"></param>
        /// <param name="ProgressTextFormat"></param>
        public static void UpdateWebtoonInfo(WebtoonInfo webtoonInfo, string ProgressTextFormat)
        {
            Agent agent = Agent.Instance;

            Parser.Parser parser     = Parser.Parser.Instance;
            WebtoonKey    webtoonKey = new WebtoonKey(webtoonInfo.WebtoonTitleId);

            //comic.naver.com에서 최신 회차의 EpisodeNo를 불러옵니다.
            agent.LoadPage(webtoonKey.BuildUrl());
            int latestEpisodeNo = int.Parse(parser.GetLatestEpisodeNo());
            //webtoonInfo중 가장 마지막 회차의 EpisodeNo를 불러옵니다.
            int lastEpisodeNo = webtoonInfo.GetLastEpisodeNo();

            //웹툰 정보를 업데이트합니다.
            for (int episodeNo = lastEpisodeNo + 1; episodeNo <= latestEpisodeNo; episodeNo++)
            {
                EpisodeKey episodeKey = new EpisodeKey(webtoonKey.TitleId, episodeNo);
                agent.LoadPage(episodeKey.BuildUrl());
                string currentEpisodeNo = parser.GetCurrentEpisodeNo();
                if (!currentEpisodeNo.Equals(episodeNo.ToString()))
                {
                    //비어있는 번호 건너뛰기
                    continue;
                }
                string   episodeTitle = parser.GetEpisodeTitle();
                string   episodeDate  = parser.GetEpisodeDate();
                string[] imageUrls    = parser.GetComicContentImageUrls();
                webtoonInfo.Episodes.Add(episodeNo, new EpisodeInfo(episodeKey, episodeTitle, imageUrls, episodeDate));
                ProgressChangedEvent(string.Format(ProgressTextFormat,
                                                   webtoonInfo.WebtoonTitle,
                                                   webtoonInfo.WebtoonTitleId,
                                                   (episodeNo).ToString("D" + latestEpisodeNo.ToString().Length.ToString()),
                                                   latestEpisodeNo,
                                                   (decimal)(episodeNo) / latestEpisodeNo,
                                                   webtoonInfo.Episodes[episodeNo].EpisodeDate,
                                                   webtoonInfo.Episodes[episodeNo].EpisodeTitle));
            }
            Console.WriteLine();
        }
        /// <summary>
        /// <code>{0}:웹툰 제목</code>
        /// <code>{1}:웹툰 아이디</code>
        /// <code>{2}:현재 포지션</code>
        /// <code>{3}:총 작업수</code>
        /// <code>{4}:퍼센트</code>
        /// <code>{5}:회차 날짜</code>
        /// <code>{6}:회차 제목</code>
        /// <code>{7}:이미지 인덱스</code>
        /// <code>{8}:이미지 파일명</code>
        /// <code>{9}:다운받은 메가바이트 용량</code>
        /// </summary>
        /// <param name="webtoonInfo"></param>
        /// <param name="imageKeys"></param>
        /// <param name="ProgressTextFormat"></param>
        public static void Download(WebtoonInfo webtoonInfo, ImageKey[] imageKeys, string ProgressTextFormat)
        {
            IO.taskEnd = false;
            Task  t     = new Task(new Action(() => { IO.SaveFileAsync(); }));
            Agent agent = Agent.Instance;
            long  size  = 0;

            t.Start();
            for (int i = 0; i < imageKeys.Length; i++)
            {
                EpisodeKey episodeKey = new EpisodeKey(imageKeys[i].TitleId, imageKeys[i].EpisodeNo);
                agent.SetHeader("Referer", episodeKey.BuildUrl());
                byte[] buff = agent.DownloadData(webtoonInfo.Episodes[imageKeys[i].EpisodeNo].EpisodeImageUrls[imageKeys[i].ImageIndex]);

                IO.WriteAllBytes(
                    BuildImageFileFullDirectory(webtoonInfo, imageKeys[i]),
                    BuildImageFileName(webtoonInfo, imageKeys[i]),
                    buff);
                size += buff.Length;
                ProgressChangedEvent(string.Format(ProgressTextFormat,
                                                   webtoonInfo.WebtoonTitle,
                                                   webtoonInfo.WebtoonTitleId,
                                                   i + 1,
                                                   imageKeys.Length,
                                                   (double)(i + 1) / imageKeys.Length,
                                                   webtoonInfo.Episodes[imageKeys[i].EpisodeNo].EpisodeDate,
                                                   webtoonInfo.Episodes[imageKeys[i].EpisodeNo].EpisodeTitle,
                                                   imageKeys[i].ImageIndex,
                                                   BuildImageFileName(webtoonInfo, imageKeys[i]),
                                                   (double)size / 1048576
                                                   ));
            }
            IO.taskEnd = true;
            t.Wait();
            Console.WriteLine();
        }
        public static (int downloadedImageCount, long downloadedImagesSize) GetDownloadedImagesInformation(WebtoonInfo webtoonInfo)
        {
            WebtoonKey webtoonKey  = new WebtoonKey(webtoonInfo.WebtoonTitleId);
            int        count       = 0;
            long       size        = 0;
            int        lastEpisode = webtoonInfo.GetLastEpisodeNo();

            for (int episodeNo = 1; episodeNo <= lastEpisode; episodeNo++)
            {
                if (!webtoonInfo.Episodes.ContainsKey(episodeNo))
                {
                    continue;
                }
                EpisodeInfo episodeInfo = webtoonInfo.Episodes[episodeNo];
                string[]    imageUrls   = episodeInfo.EpisodeImageUrls;
                for (int imageIndex = 0; imageIndex < imageUrls.Length; imageIndex++)
                {
                    ImageKey imageKey = new ImageKey(webtoonInfo.WebtoonTitleId, episodeNo, imageIndex);
                    if (IO.Exists(BuildImageFileFullDirectory(webtoonInfo, imageKey), BuildImageFileName(webtoonInfo, imageKey)))
                    {
                        count++;
                        size += new FileInfo(BuildImageFileFullDirectory(webtoonInfo, imageKey) + "\\" + BuildImageFileName(webtoonInfo, imageKey)).Length;
                    }
                }
            }
            return(count, size);
        }
        private void Download(params string[] args)
        {
            if (args.Length == 0)
            {
                IO.PrintError("titleId를 입력해주세요");
                return;
            }
            List <WebtoonKey> keys   = new List <WebtoonKey>();
            List <string>     titles = new List <string>();

            for (int i = 0; i < args.Length; i++)
            {
                if (!int.TryParse(args[0], out _))
                {
                    IO.PrintError("titleId는 숫자입니다. : " + args[i]);
                    return;
                }
                agent.LoadPage(string.Format("https://comic.naver.com/webtoon/list.nhn?titleId={0}", args[i]));
                string title = parser.GetWebtoonTitle();
                if (title == "네이버 웹툰")
                {
                    IO.PrintError("존재하지 않는 titleId입니다. : " + args[i]);
                    return;
                }
                IO.Print(string.Format("{2}. {1}($${0}$cyan$) 대기..", args[i], title, i + 1), true, true);
                titles.Add(title);
                keys.Add(new WebtoonKey(args[i]));
            }
            for (int i = 0; i < keys.Count; i++)
            {
                IO.Print("");
                WebtoonInfo webtoonInfo;
                if (IO.Exists("Cache", keys[i].TitleId + ".json"))
                {
                    webtoonInfo = JsonConvert.DeserializeObject <WebtoonInfo>(IO.ReadTextFile("Cache", keys[i].TitleId + ".json"));
                    int latest = int.Parse(parser.GetLatestEpisodeNo());
                    int last   = webtoonInfo.GetLastEpisodeNo();
                    IO.Print(string.Format("{2}. {0}($${1}$cyan$) URl 캐시를 불러왔습니다.", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
                    IO.Print(string.Format("{2}. {0}($${1}$cyan$) 업데이트된 회차를 확인합니다.. ", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
                    if (latest != last)
                    {
                        IO.Print(string.Format("{4}. {0}($${1}$cyan$) URl 캐시를 업데이트합니다.. [no($${2}$cyan$) ~ no($${3}$cyan$)]", webtoonInfo.WebtoonTitle, keys[i].TitleId, last + 1, latest, i + 1), true, true);
                        Downloader.UpdateWebtoonInfo(webtoonInfo, (i + 1).ToString() + " {0}($${1}$cyan$) [{2}/{3}] ($${4:P}$green$) [{5}]");
                        IO.Print(string.Format("{2}. {0}($${1}$cyan$) URl 캐시에 업데이트된 회차를 추가하였습니다.", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
                    }
                    else
                    {
                        IO.Print(string.Format("{2}. {0}($${1}$cyan$) 업데이트된 회차가 없습니다. ", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
                    }
                    var tuple = Downloader.GetDownloadedImagesInformation(webtoonInfo);

                    if (tuple.downloadedImageCount != 0)
                    {
                        IO.Print(string.Format("{4}. {0}($${1}$cyan$) 이미 다운로드된 이미지 $${2}$cyan$장 ($${3:0.00}$blue$ MB)  ", webtoonInfo.WebtoonTitle, keys[i].TitleId, tuple.downloadedImageCount, (double)tuple.downloadedImagesSize / 1048576, i + 1), true, true);
                    }
                }
                else
                {
                    webtoonInfo = new WebtoonInfo(keys[i], titles[i]);
                    IO.Print(string.Format("{2}. {0}($${1}$cyan$) URl 캐시를 생성합니다.", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
                    Downloader.BuildWebtoonInfo(webtoonInfo, (i + 1).ToString() + ". {0}($${1}$cyan$) [{2}/{3}] ($${4:P}$green$) [{5}]");
                    IO.Print(string.Format("{2}. {0}($${1}$cyan$) URl 캐시를 생성하였습니다..", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
                }
                IO.WriteTextFile("Cache", keys[i].TitleId + ".json", JsonConvert.SerializeObject(webtoonInfo));

                ImageKey[] imageKeys = Downloader.BuildImageKeysToDown(webtoonInfo);
                if (imageKeys.Length == 0)
                {
                    IO.Print(string.Format("{2}. {0}($${1}$cyan$) 모든 이미지가 다운로드되었습니다..추가로 다운로드할 이미지가 존재하지 않습니다.", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
                    return;
                }
                IO.Print(string.Format("{2}. {0}($${1}$cyan$) 다운로드를 시작합니다. ", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
                try
                {
                    Downloader.Download(webtoonInfo, imageKeys, (i + 1).ToString() + ". {0}($${1}$cyan$) [{2}/{3}] ($${9:0.00}$blue$ MB) ($${4:P}$green$) [{5}]");
                    IO.Print(string.Format("{2}. {0}($${1}$cyan$) 다운로드 완료", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
                }
                catch (Exception e)
                {
                    IO.PrintError(e.Message);
                }
            }
        }
示例#11
0
        public override void Start(params string[] args)
        {
            if (args.Length == 0)
            {
                IO.PrintError("titleId를 입력해주세요");
                return;
            }
            List <WebtoonKey> keys   = new List <WebtoonKey>();
            List <string>     titles = new List <string>();

            for (int i = 0; i < args.Length; i++)
            {
                if (!int.TryParse(args[i], out _))
                {
                    IO.PrintError("titleId는 숫자입니다. : " + args[i]);
                    return;
                }
                agent.LoadPage(string.Format("https://comic.naver.com/webtoon/list.nhn?titleId={0}", args[i]));
                string title = parser.GetWebtoonTitle();
                if (title == "네이버 웹툰")
                {
                    IO.PrintError("존재하지 않는 titleId입니다. : " + args[i]);
                    return;
                }
                IO.Print(string.Format("{2}. {1}($${0}$cyan$) 대기..", args[i], title, i + 1), true, true);
                keys.Add(new WebtoonKey(args[i]));
                titles.Add(title);
            }
            for (int i = 0; i < keys.Count; i++)
            {
                Downloader downloader;
                IO.Print("");
                WebtoonInfo webtoonInfo;

                if (IO.Exists("Cache", keys[i].TitleId + ".json"))
                {
                    webtoonInfo = JsonConvert.DeserializeObject <WebtoonInfo>(IO.ReadTextFile("Cache", keys[i].TitleId + ".json"));
                    downloader  = new Downloader(webtoonInfo, config);
                    int latest = int.Parse(parser.GetLatestEpisodeNo());
                    int last   = webtoonInfo.GetLastEpisodeNo();
                    IO.Print(string.Format("{2}. {0}($${1}$cyan$) URl 캐시를 불러왔습니다.", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
                    IO.Print(string.Format("{2}. {0}($${1}$cyan$) 업데이트된 회차를 확인합니다.. ", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
                    if (latest != last)
                    {
                        IO.Print(string.Format("{4}. {0}($${1}$cyan$) URl 캐시를 업데이트합니다.. [no($${2}$cyan$) ~ no($${3}$cyan$)]", webtoonInfo.WebtoonTitle, keys[i].TitleId, last + 1, latest, i + 1), true, true);
                        downloader.UpdateWebtoonInfo((i + 1).ToString() + ". {0}($${1}$cyan$) [{2}/{3}] ($${4:P}$green$) [{5}]", progress);
                        Console.WriteLine();
                        IO.Print(string.Format("{2}. {0}($${1}$cyan$) URl 캐시에 업데이트된 회차를 추가하였습니다.", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
                    }
                    else
                    {
                        IO.Print(string.Format("{2}. {0}($${1}$cyan$) 업데이트된 회차가 없습니다. ", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
                    }
                    if (string.IsNullOrWhiteSpace(webtoonInfo.WebtoonWriter))
                    {
                        EpisodeKey episodeKey = new EpisodeKey(keys[i].TitleId, 1);
                        agent.LoadPage(episodeKey.BuildUrl());
                        string webtoonWriter = parser.GetWebtoonWriter();
                        webtoonInfo.WebtoonWriter = webtoonWriter;
                    }
                    var tuple = downloader.GetDownloadedImagesInformation();

                    if (tuple.downloadedImageCount != 0)
                    {
                        IO.Print(string.Format("{4}. {0}($${1}$cyan$) 이미 다운로드된 이미지 $${2}$cyan$장 ($${3:0.00}$blue$ MB)  ", webtoonInfo.WebtoonTitle, keys[i].TitleId, tuple.downloadedImageCount, (double)tuple.downloadedImagesSize / 1048576, i + 1), true, true);
                    }
                }
                else
                {
                    webtoonInfo = new WebtoonInfo(keys[i], titles[i]);
                    downloader  = new Downloader(webtoonInfo, config);
                    IO.Print(string.Format("{2}. {0}($${1}$cyan$) URl 캐시를 생성합니다.", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
                    downloader.UpdateWebtoonInfo((i + 1).ToString() + ". {0}($${1}$cyan$) [{2}/{3}] ($${4:P}$green$) [{5}]", progress);
                    Console.WriteLine("");
                    IO.Print(string.Format("{2}. {0}($${1}$cyan$) URl 캐시를 생성하였습니다..", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
                }

                if (string.IsNullOrWhiteSpace(webtoonInfo.WebtoonWriter))
                {
                    EpisodeKey episodeKey = new EpisodeKey(keys[i].TitleId, 1);
                    agent.LoadPage(episodeKey.BuildUrl());
                    string webtoonWriter = parser.GetWebtoonWriter();
                    webtoonInfo.WebtoonWriter = webtoonWriter;
                }

                IO.WriteTextFile("Cache", keys[i].TitleId + ".json", JsonConvert.SerializeObject(webtoonInfo));

                ImageKey[] imageKeys = downloader.BuildImageKeysToDown();
                if (imageKeys.Length == 0)
                {
                    IO.Print(string.Format("{2}. {0}($${1}$cyan$) 모든 이미지가 다운로드되었습니다..추가로 다운로드할 이미지가 존재하지 않습니다.", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
                    return;
                }
                IO.Print(string.Format("{2}. {0}($${1}$cyan$) 다운로드를 시작합니다. ", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
                var task = Task.Run(() => downloader.DownloadAsync(imageKeys, (i + 1).ToString() + ". {0}($${1}$cyan$) [{2}/{3}] ($${9:0.00}$blue$ MB) ($${4:P}$green$) [{5}]", progress));
                task.Wait();
                Console.WriteLine();
                IO.Print(string.Format("{2}. {0}($${1}$cyan$) 다운로드 완료", webtoonInfo.WebtoonTitle, keys[i].TitleId, i + 1), true, true);
            }
        }
 public FileNameBuilder(WebtoonInfo webtoonInfo, Config config)
 {
     this.webtoonInfo = webtoonInfo;
     this.config      = config;
 }