From f3f3045ebf5d9fb3a74f30c057cc579619547814 Mon Sep 17 00:00:00 2001 From: ithillad Date: Sun, 11 May 2025 18:58:43 +0200 Subject: [PATCH] commit --- Bomtoon_JS/bomtoon.js | 41 ++++++ Bomtoon_JS/bomtoon_canvas.js | 53 ++++++++ Bomtoon_JS/canvas_count.js | 14 ++ Bomtoon_JS/get_count.js | 24 ++++ Bomtoon_JS/search_tag.js | 29 ++++ KakaoPage_JS/kakaopage.js | 31 +++++ __pycache__/prerequisite.cpython-311.pyc | Bin 0 -> 4896 bytes bomtoon.py | 72 ++++++++++ bomtoon_search_tag.py | 25 ++++ check_raw.py | 50 +++++++ compare_bomtoon_count.py | 34 +++++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 152 bytes .../__pycache__/converter.cpython-311.pyc | Bin 0 -> 12590 bytes converter/converter.py | 36 +++-- data/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 147 bytes .../bomtoon_request.cpython-311.pyc | Bin 0 -> 861 bytes data/__pycache__/kakao_cookie.cpython-311.pyc | Bin 0 -> 738 bytes .../__pycache__/kakao_request.cpython-311.pyc | Bin 0 -> 2538 bytes .../__pycache__/path_constant.cpython-311.pyc | Bin 0 -> 891 bytes data/__pycache__/special_list.cpython-311.pyc | Bin 0 -> 2462 bytes .../webtoon_request.cpython-311.pyc | Bin 0 -> 1531 bytes data/kakao_cookie.py | 30 ++-- data/kakao_request.py | 14 +- data/path_constant.py | 1 + data/special_list.py | 128 +++++++++++------- data/webtoon_request.py | 16 --- .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 154 bytes .../__pycache__/bomtoon.cpython-311.pyc | Bin 0 -> 3593 bytes .../__pycache__/decrypt.cpython-311.pyc | Bin 0 -> 3208 bytes .../__pycache__/downloader.cpython-311.pyc | Bin 0 -> 11016 bytes .../__pycache__/happymh.cpython-311.pyc | Bin 0 -> 8433 bytes .../__pycache__/kakao_webtoon.cpython-311.pyc | Bin 0 -> 10828 bytes .../__pycache__/webtoon_com.cpython-311.pyc | Bin 0 -> 9273 bytes downloaders/bomtoon.py | 62 --------- downloaders/decrypt.py | 5 +- downloaders/downloader.py | 21 ++- downloaders/kakao_webtoon.py | 26 ++-- dungeon_odyssey.py | 58 ++++++++ helper.py | 2 +- helper/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 149 bytes .../get_kakao_list.cpython-311.pyc | Bin 0 -> 962 bytes .../missing_episode.cpython-311.pyc | Bin 0 -> 1461 bytes .../missing_images.cpython-311.pyc | Bin 0 -> 2409 bytes .../__pycache__/prerequisite.cpython-311.pyc | Bin 0 -> 4903 bytes helper/get_kakao_list.py | 18 +++ prerequisite.py => helper/prerequisite.py | 2 +- main.py | 101 +++++++------- rename.py | 36 +++-- 48 files changed, 686 insertions(+), 243 deletions(-) create mode 100644 Bomtoon_JS/bomtoon.js create mode 100644 Bomtoon_JS/bomtoon_canvas.js create mode 100644 Bomtoon_JS/canvas_count.js create mode 100644 Bomtoon_JS/get_count.js create mode 100644 Bomtoon_JS/search_tag.js create mode 100644 KakaoPage_JS/kakaopage.js create mode 100644 __pycache__/prerequisite.cpython-311.pyc create mode 100644 bomtoon.py create mode 100644 bomtoon_search_tag.py create mode 100644 check_raw.py create mode 100644 compare_bomtoon_count.py create mode 100644 converter/__pycache__/__init__.cpython-311.pyc create mode 100644 converter/__pycache__/converter.cpython-311.pyc create mode 100644 data/__pycache__/__init__.cpython-311.pyc create mode 100644 data/__pycache__/bomtoon_request.cpython-311.pyc create mode 100644 data/__pycache__/kakao_cookie.cpython-311.pyc create mode 100644 data/__pycache__/kakao_request.cpython-311.pyc create mode 100644 data/__pycache__/path_constant.cpython-311.pyc create mode 100644 data/__pycache__/special_list.cpython-311.pyc create mode 100644 data/__pycache__/webtoon_request.cpython-311.pyc create mode 100644 downloaders/__pycache__/__init__.cpython-311.pyc create mode 100644 downloaders/__pycache__/bomtoon.cpython-311.pyc create mode 100644 downloaders/__pycache__/decrypt.cpython-311.pyc create mode 100644 downloaders/__pycache__/downloader.cpython-311.pyc create mode 100644 downloaders/__pycache__/happymh.cpython-311.pyc create mode 100644 downloaders/__pycache__/kakao_webtoon.cpython-311.pyc create mode 100644 downloaders/__pycache__/webtoon_com.cpython-311.pyc delete mode 100644 downloaders/bomtoon.py create mode 100644 dungeon_odyssey.py create mode 100644 helper/__pycache__/__init__.cpython-311.pyc create mode 100644 helper/__pycache__/get_kakao_list.cpython-311.pyc create mode 100644 helper/__pycache__/missing_episode.cpython-311.pyc create mode 100644 helper/__pycache__/missing_images.cpython-311.pyc create mode 100644 helper/__pycache__/prerequisite.cpython-311.pyc create mode 100644 helper/get_kakao_list.py rename prerequisite.py => helper/prerequisite.py (97%) diff --git a/Bomtoon_JS/bomtoon.js b/Bomtoon_JS/bomtoon.js new file mode 100644 index 0000000..23f80b8 --- /dev/null +++ b/Bomtoon_JS/bomtoon.js @@ -0,0 +1,41 @@ +async function downloadImages(blobUrls) { + for (let i = 0; i < blobUrls.length; i++) { + let response = await fetch(blobUrls[i]); + let blob = await response.blob(); + + let blobUrlObject = URL.createObjectURL(blob); + + let indexStr = String(i).padStart(3, "0"); // 生成 3 位数格式 + let filename = `${indexStr}.webp`; // e.g., 001.webp + + let a = document.createElement("a"); + a.href = blobUrlObject; + a.download = filename; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + + URL.revokeObjectURL(blobUrlObject); + console.log(`Downloaded: ${filename}`); + + await new Promise(resolve => setTimeout(resolve, 500)); // 避免 Chrome 限制 + } + + const div = document.querySelector('div.printView > div:not(style)'); + const div2 = Array.from(div.children).filter(el => el.tagName.toLowerCase() === 'div')[1]; + const div2_1_1 = div2.querySelector('div:nth-of-type(1) > div:nth-of-type(1)'); + const count = Array.from(div2_1_1.children).filter(el => { + return el.tagName.toLowerCase() === 'div' && + el.hasAttribute('width') && + el.hasAttribute('height'); + }).length; + + console.log("div2.1.1 下的
数量为:", count); + console.log(document.title); +} + +const blobs = [...document.querySelectorAll("img")] + .map(el => el.src) + .filter(src => src.startsWith("blob:")); + +downloadImages(blobs); \ No newline at end of file diff --git a/Bomtoon_JS/bomtoon_canvas.js b/Bomtoon_JS/bomtoon_canvas.js new file mode 100644 index 0000000..7a4614c --- /dev/null +++ b/Bomtoon_JS/bomtoon_canvas.js @@ -0,0 +1,53 @@ +async function downloadCanvasImages() { + let seenCanvases = new Set(); // 存储已经下载过的 Canvas,避免重复下载 + let lastScrollTop = 0; + + while (true) { + console.log("🔽 正在下载当前屏幕的所有 Canvas..."); + + // 获取所有 并下载 + document.querySelectorAll("canvas").forEach((canvas, index) => { + if (!seenCanvases.has(canvas)) { // 确保不重复下载 + seenCanvases.add(canvas); + + let imgData = canvas.toDataURL("image/webp"); // 转为 Base64 webp + let a = document.createElement("a"); + a.href = imgData; + a.download = `${String(seenCanvases.size).padStart(3, "0")}.webp`; // 命名 001, 002, ... + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + + console.log(`✅ 下载: ${a.download}`); + } + }); + + // 记录滚动前的位置 + lastScrollTop = window.scrollY; + + // 向下滚动 1 屏 + window.scrollBy(0, window.innerHeight); + await new Promise(resolve => setTimeout(resolve, 3000)); // 等待 3 秒加载新 Canvas + + // 如果滚动到底,停止执行 + if (window.scrollY === lastScrollTop) { + console.log("🎉 已滚动到底,所有 Canvas 下载完成!"); + break; + } + } + + const div = document.querySelector('div.printView > div:not(style)'); + const div2 = Array.from(div.children).filter(el => el.tagName.toLowerCase() === 'div')[1]; + const div2_1_1_1 = div2.querySelector('div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1)'); + const count = Array.from(div2_1_1_1.children).filter(el => { + return el.tagName.toLowerCase() === 'div' && + el.hasAttribute('width') && + el.hasAttribute('height'); + }).length; + + console.log("div2.1.1.1 下的
数量为:", count); + console.log(document.title); +} + +// 运行脚本 +downloadCanvasImages(); \ No newline at end of file diff --git a/Bomtoon_JS/canvas_count.js b/Bomtoon_JS/canvas_count.js new file mode 100644 index 0000000..ecc6c67 --- /dev/null +++ b/Bomtoon_JS/canvas_count.js @@ -0,0 +1,14 @@ +const divs = document.querySelectorAll('div[class^="CanvasViewer__Container"]'); +console.log("符合条件的 div 总数:", divs.length); + +const div = document.querySelector('div.printView > div:not(style)'); +const div2 = Array.from(div.children).filter(el => el.tagName.toLowerCase() === 'div')[1]; +const div2_1_1_1 = div2.querySelector('div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1)'); +const count = Array.from(div2_1_1_1.children).filter(el => { + return el.tagName.toLowerCase() === 'div' && + el.hasAttribute('width') && + el.hasAttribute('height'); +}).length; + +console.log("div2.1.1.1 下的
数量为:", count); +console.log(document.title); \ No newline at end of file diff --git a/Bomtoon_JS/get_count.js b/Bomtoon_JS/get_count.js new file mode 100644 index 0000000..64fefd1 --- /dev/null +++ b/Bomtoon_JS/get_count.js @@ -0,0 +1,24 @@ +(() => { + const count = document.querySelectorAll('div[class^="ImageContainer__Container"]').length; + const existing = localStorage.getItem("divCountList") || ""; + localStorage.setItem("divCountList", existing + count + "\n"); + + console.log("✅ Count saved:", count); + })(); + + + + +(() => { + const data = localStorage.getItem("divCountList") || ""; + const blob = new Blob([data], { type: "text/plain" }); + const link = document.createElement("a"); + + link.href = URL.createObjectURL(blob); + link.download = "div_counts.txt"; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + + console.log("📦 File downloaded as div_counts.txt"); + })(); \ No newline at end of file diff --git a/Bomtoon_JS/search_tag.js b/Bomtoon_JS/search_tag.js new file mode 100644 index 0000000..e7d51cc --- /dev/null +++ b/Bomtoon_JS/search_tag.js @@ -0,0 +1,29 @@ +const container = document.querySelector('.fmxVRH'); + +if (container) { + // 获取 container 下所有 size="16" 的 div 元素 + const targetDivs = container.querySelectorAll('div[size="16"]'); + + // 提取每个 div 的文本内容 + const contents = Array.from(targetDivs).map(div => div.textContent.trim()); + const contentString = contents.join('\n'); + // 保存到 localStorage + localStorage.setItem('searchTag', contentString); +} else { + console.log("没有找到 class 为 fmxVRH 的元素"); +} + +(() => { + const data = localStorage.getItem("searchTag") || ""; + const blob = new Blob([data], { type: "text/plain" }); + const link = document.createElement("a"); + + link.href = URL.createObjectURL(blob); + link.download = "tag_results.txt"; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + + console.log("📦 File downloaded as tag_results.txt"); +})(); +localStorage.removeItem('searchTag'); \ No newline at end of file diff --git a/KakaoPage_JS/kakaopage.js b/KakaoPage_JS/kakaopage.js new file mode 100644 index 0000000..55fe9d1 --- /dev/null +++ b/KakaoPage_JS/kakaopage.js @@ -0,0 +1,31 @@ +async function downloadImages(urls) { + for (let i = 0; i < urls.length; i++) { + const url = urls[i]; + + try { + // 尝试抓取并转成 Blob + const response = await fetch(url, { mode: 'cors' }); + const blob = await response.blob(); + + // 创建临时链接并强制下载 + const blobUrl = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = blobUrl; + a.download = `image_${i + 1}.jpg`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(blobUrl); // 清理资源 + + console.log(`✅ 下载成功: ${url}`); + } catch (err) { + console.error(`❌ 下载失败: ${url}`, err); + } + } + } + + const links = Array.from(document.querySelectorAll('img')) + .map(img => img.src) + .filter(src => src.startsWith('https://page-edge.kakao.com/sdownload/resource?kid=')); + + downloadImages(links); \ No newline at end of file diff --git a/__pycache__/prerequisite.cpython-311.pyc b/__pycache__/prerequisite.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..81dc7a3c404bbf11c1c342d01a001e4753421483 GIT binary patch literal 4896 zcmbtX-A^0Y6~FW0uNVgt&$1*CGfkkxO)!BF8Vm^`HJe2tCJE#Nm&o#Z25@R@@10=@ z#8f<_(pKUDRZ){ovI?!LY**<6FOmAtht2*0)eId8YbvBlRi*CJdX-k9JoVf$HXiJ> zR(t2pxo7U2d*PO;OOnp$mn1&5*)svJ3~X`w?}VWF&@}7h{UMfiITHSGH4AR%F9dWdR6QWqPx$Qs0_7Ms;T(aEQ@~za>aM0j9CtJrkCgpU6szTY}wNU zWA+|OIJ5qjFxOYb`AWSqS@~`es0{rKeNLNYC?q=y5(wQZelb%nSE>uv@C<(M=gfX> zD<7{s7ZCh1HnhV9#e^uwlheEq$6`!Q;e`OdST{7AmKXNA__m$*+jxP9iUKdq#bTl) zP0b||3x1zfx5Z@2E9%aW2vN2(j^gl;CX(hRSZmC!>M~PKWXyn!fo**Ih9IIrm={{kLThN zeBYm5&=2P)7>Onl5s@q)BBtX~N)RQ?fhMvy(w~4VqihrZV&v(_XQAhz9I{%%RGupK zhOD!3(|K&ec}#V-XwH@#vN*z2w)O00>-mk=^J?pW);f?wtcwgGRB?YV>uK5aoZRr7 zR6XsQr#*+5x-j)RrUb5R2F5l5V`|{07PzVQ+|qh(kawlI^ZbIWG6mDXBJLlS)`CC{PiV;#MWS0#* zec!;7T(mf_pdxR!OtAdS5I?y~Wh|JGs+ay>9M3h*& zKxa@zM1a#PlzY|!mqBKIX5F_|;?McRK_XTfG?C9=^xk3G=rIQpl>quPA=NCzdwTmA%l==as{XC)d1FHL?=Dw)7FK)T(AGs8- zUv+nA?heJ>u|)*0DlOfF8dY_NG-{jLR_9pmjegR{Rc<0>JeWOz>3^?-WWp zkE2c`vGeqOW&+KF0Z)M8L;vODNG{?Gp%#Se_o2&Tpvzj0t_hSpAsgvRetj^uX^AzV z+?HW0p?m^nxWxXHP1^JAJT;H<^U9Ev!yeKpyl9z|r`pd!-eDJ$v6KL@Y|%a=%V{al z(Xm)FJ|pr|@r1}nWqz@ipN~p>G9~jFWU?E;VhS!J@GqiY_;(*iZiH9a2v#pv3Pz8L=X@UO1? z=8DRl*0|FOcX~T#AtM-2U}!POcSPSm2-)ka#BD?5sUWqz{ZBipIhsS1rF)NT*R8;(d=iGLfvw5MA)}Sj1g^XgUZ1+sH**hWAB89+fV_ zu01?7$fI9&4sy&JdkwH}ICikX_NIX$d?Q8pCWGgXUTR_93^;&(Z9jCWnfbce0(@og zUI2JhpOCIncdb>Yq~BJZlIm5bq*qB2d9Mg*a2~Ok(y@%D`4KST8m&Ull>o7yYlB%Y6Tn@ zI4WCck@oXDlz)ym`EUR5=_1Xa*yVq|L-`=m7K5zLCZn@r-nH1Um`+4vqE4qI$e=~? zX4lCWQ5PQqb-FDn&WGTQZU@FlruTcA+WgRB?tn>Ja{_MZY~Ih5=EE*a-**T|rBGYr z=qpQxovywd0#YdzX6yRaTObqG0@su-QjLuEq{^MrxKj#uD(mggyxp7L{ta*c@5jG7 zqk1oE-pi|(v&|jrZEADx>W5og{i9*U+p*rDasiDCC|sZ{;kLk6T9v*Lt?#FrQ%W6|<0cfP*sU66Pm?{)yp0aBZnmD0ew4*He^o)S9&gMJtLbvH#T~1C}X$Op4(c_ZMEx; z)^$hu@#IF=q|!CH&oCi~ISTVI49Zu7J7B81jU0&+@sBY19Y(UP9}=Q+)KB^&kZMbE zG$~`U+4&rur2a+@ZKMzezA;(-mnfg%8%GR#|7=Q_ONbY6KfD6uBk7MoAdpg27CDxU zD~lQwbIl^xvT*(s91mr%}0aZg|XH2cYsgH+!WIC(>} VpB%GL?Q1d|$|?94?;yr{{|(O)E~x+j literal 0 HcmV?d00001 diff --git a/bomtoon.py b/bomtoon.py new file mode 100644 index 0000000..7b587d9 --- /dev/null +++ b/bomtoon.py @@ -0,0 +1,72 @@ +from pathlib import Path +import shutil + +BOMTOON = { + # "漫画名": (判断目录是否不为temp,起始index,修正index) + "鄰居是公會成員": (True, 0, 1), + "PAYBACK": (True, 0, 0), + "1995青春報告": (True, 0, 0), + "Unsleep": (True, 0, 1), + "Backlight": (True, 0, 1), + "鬼夜曲": (True, 65, -3), + "披薩外送員與黃金宮": (True, 0, -1), + "No Moral": (True, 87, 0), + # "No Moral": (True, 0, 1), + "易地思之": (True, 0, 1), + "監禁倉庫": (True, 0, 1), + "棋子的世界": (True, 0, 1), + "夢龍傳": (True, 0, 1), + "融冰曲線": (True, 0, 1), +} + +# current = "鄰居是公會成員" +# current = "Unsleep" +# current = "Backlight" +# current = "1995青春報告" +current = "鬼夜曲" +# current = "No Moral" +# current = "易地思之" +# current = "監禁倉庫" +# current = "披薩外送員與黃金宮" +# current = "PAYBACK" +# current = "夢龍傳" +# current = "棋子的世界" +# current = "融冰曲線" + +bomtoon_path = Path('E:/') / 'Webtoon' / current if BOMTOON[current][0] else Path('E:/') / 'Temp_Webtoon' / current + +def find_next_index(index_list, start_index): + if len(index_list) == 0: + return 0 + index_list = sorted(set(index_list)) # 先去重并排序 + for i in range(index_list[0], index_list[-1]): # 遍历从最小值到最大值 + if i not in index_list and i > start_index: + return i # 返回第一个缺失的数字 + return index_list[-1] + 1 + +def create_dir(index, bomtoon_name): + name = str(index) + '.' + '第' + str(index + BOMTOON[bomtoon_name][2]) + '話' + # name = str(index) + '.' + '外傳 第' + str(index - 86) + '話' + path = Path(bomtoon_path) / name + path.mkdir(parents=True, exist_ok=True) + print(f"create {path}") + return path + +def move_all_webps(dest_dir: Path): + download_dir = Path('C:/') / 'Users' / 'ithil' / 'Downloads' + if not dest_dir.exists(): + dest_dir.mkdir(parents=True) # 如果目标目录不存在,则创建 + + for file in download_dir.iterdir(): + if file.is_file() and file.suffix.lower() == ".webp": # 只移动webp文件 + shutil.move(str(file), str(dest_dir)) # 移动文件 + +index_list = [] +for first_level_path in bomtoon_path.iterdir(): + if first_level_path.is_dir(): + index_list.append(int(first_level_path.name.split(".")[0])) + +index = find_next_index(index_list, BOMTOON[current][1]) +new_dir = create_dir(index, current) +move_all_webps(new_dir) + diff --git a/bomtoon_search_tag.py b/bomtoon_search_tag.py new file mode 100644 index 0000000..c874cb8 --- /dev/null +++ b/bomtoon_search_tag.py @@ -0,0 +1,25 @@ +from pathlib import Path + +txt1 = Path('E:/') / "basic.txt" +txt2 = Path('E:/') / "bad.txt" + +with txt1.open("r", encoding="utf-8") as f: + titles1 = [line.strip() for line in f if line.strip()] + +with txt2.open("r", encoding="utf-8") as f: + titles2 = [line.strip() for line in f if line.strip()] + +result = [] +for title in titles1: + if title not in titles2: + result.append(title) + +txt3 = Path('E:/') / "good.txt" +with txt3.open("r", encoding="utf-8") as f: + titles3 = [line.strip() for line in f if line.strip()] + +result_new = [] +for title in result: + if title in titles3: + result_new.append(title) +print(result_new) \ No newline at end of file diff --git a/check_raw.py b/check_raw.py new file mode 100644 index 0000000..16b621c --- /dev/null +++ b/check_raw.py @@ -0,0 +1,50 @@ +from pathlib import Path +import os + +def get_all_sub_paths(path: Path): + if not path.exists() or not path.is_dir(): + print(f"路径 {path} 无效或不是一个目录") + return + + sub_path_list = [] + + for travel_dir_path in path.iterdir(): + if travel_dir_path.is_dir(): + sub_path_list.append(travel_dir_path) + for first_level_path in travel_dir_path.iterdir(): + if first_level_path.is_dir(): + sub_path_list.append(first_level_path) + for second_level_path in first_level_path.iterdir(): + if second_level_path.is_dir(): + sub_path_list.append(second_level_path) + print("second level dir") + return sub_path_list + +def traverse_directory(paths): + for path in paths: + dir_name = str(path).replace("D:\\Photo\\", "") + network_path = Path(r"\\TRUENAS\Media\Photo\Photos") / dir_name + file_count_local = 0 + file_count_network = 0 + for sub_local in path.iterdir(): + if sub_local.is_file(): + file_count_local +=1 + for sub_network in network_path.iterdir(): + if sub_network.is_file(): + file_count_network +=1 + print(f"目录: {path.name}") + if file_count_local == file_count_network: + print(f"文件总数: {file_count_local}") + else: + print("!!!!!!!!文件数量不一致!!!!!!!!") + + +# path = Path('D:/') / 'Photo' +# path = Path('//TRUENAS') / 'Media' / 'Photo' / 'Photos' +path = r"\\TRUENAS\Media\Photo\Photos" + + +all_paths = get_all_sub_paths(Path(path)) +# for path in all_paths: +# print(path) +traverse_directory(all_paths) \ No newline at end of file diff --git a/compare_bomtoon_count.py b/compare_bomtoon_count.py new file mode 100644 index 0000000..aec86f8 --- /dev/null +++ b/compare_bomtoon_count.py @@ -0,0 +1,34 @@ +from pathlib import Path + +BOMTOON = "Unsleep" +PATH = Path('E:/') / 'Webtoon' / BOMTOON + +folders = [ + (int(p.name.split('.')[0]), p) # 取前缀数字用于排序 + for p in PATH.iterdir() + if p.is_dir() and p.name.split('.')[0].isdigit() +] + +folders.sort(key=lambda x: x[0]) + +webp_counts = [] + +for index, path in folders: + count = len([f for f in path.iterdir() if f.is_file() and f.suffix.lower() == ".webp"]) + webp_counts.append(count) + +# 输出最终结果 +print(webp_counts) + +txt = PATH / "div_counts.txt" + +with txt.open("r") as f: + counts = [int(line.strip()) for line in f if line.strip()] + +print(counts) + +for i, (a, b) in enumerate(zip(counts, webp_counts)): + if a != b + 1: + print(f"❌ 第 {i} 项不同: list1 = {a}, list2 + 1 = {b + 1}") + else: + print(f"✅ 第 {i} 项相同: list1 = {a}, list2 + 1 = {b + 1}") \ No newline at end of file diff --git a/converter/__pycache__/__init__.cpython-311.pyc b/converter/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..520cffe2e3fc354ebd020c395544950c860c49a7 GIT binary patch literal 152 zcmZ3^%ge<81j22-X(0MBh=2h`DC095kTIPhg&~+hlhJP_LlF~@{~09tOFPvnCZH%k zD>b>KIL5a!JT<8#KR+)fIX|x~wWuVuC?-BWGcU6wK3=b&@)w6qZhlH>PO4oID^M@U W^kRM>@qw9r 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 literal 0 HcmV?d00001 diff --git a/converter/converter.py b/converter/converter.py index a1558b6..fa6ecea 100644 --- a/converter/converter.py +++ b/converter/converter.py @@ -3,25 +3,28 @@ from pathlib import Path import shutil from PIL import Image from data.path_constant import ANDROID_ASSETS, DOWNLOAD_DIR, NETWORK_DIR +from data.special_list import BOMTOON class WebtoonConverter: def __init__(self, webtoon_path: Path): self.webtoon_path = webtoon_path self.webtoon_path_network = NETWORK_DIR / webtoon_path.name + self.thumbnail = '' self.img_extensions = {'.png', '.jpg', '.jpeg', '.webp'} - def do_convert(self): + def do_convert(self): if self.webtoon_path.is_dir() and self.has_new_episode(): print(self.webtoon_path) - self.copy_information() + self.copy_information() for item_path in self.webtoon_path.iterdir(): if item_path.is_dir(): episode_path = item_path - if self.is_new_episode(episode_path): + if self.is_new_episode(episode_path) and self.is_not_empty(episode_path): print(f"new episode: {episode_path}") - self.delete_over_width_image(episode_path) + if self.webtoon_path.name not in BOMTOON: + self.delete_over_width_image(episode_path) self.concat_images(episode_path) - elif item_path.suffix.lower() in self.img_extensions: + elif item_path.name == self.thumbnail: thumbnail_path = item_path self.copy_thumbnail(thumbnail_path) @@ -46,13 +49,15 @@ class WebtoonConverter: local_information = json.load(json_file) with open(info_path_network, "r", encoding='utf-8') as json_file: network_information = json.load(json_file) + + self.thumbnail = local_information["thumbnail"] if ( local_information["title"] == network_information["title"] and local_information["author"] == network_information["author"] and local_information["tag"] == network_information["tag"] and local_information["description"] == network_information["description"] and - local_information["thumbnail_name"] == network_information["thumbnail_name"] + local_information["thumbnail"] == network_information["thumbnail"] ): copy_necessary = False @@ -104,7 +109,7 @@ class WebtoonConverter: "author": existing_information["author"], "tag": tag, "description": existing_information["description"], - "thumbnail_name": existing_information["thumbnail_name"] + "thumbnail": existing_information["thumbnail"] } with open(path, 'w', encoding='utf-8') as json_file: @@ -121,15 +126,23 @@ class WebtoonConverter: print(f"Source file '{thumbnail_path}' not found.") def delete_over_width_image(self, episode_path: Path): + if self.webtoon_path.name != "地下城見聞錄[UO]": + for img_path in episode_path.iterdir(): + if self._is_image_800(img_path): + img_path.unlink() + print(f"delete {img_path}") + + def delete_bomtoon_000(self, episode_path: Path): for img_path in episode_path.iterdir(): - if self._is_image_800(img_path): + if img_path.name == "000.webp": img_path.unlink() print(f"delete {img_path}") + def _is_image_800(self, image_path: Path) -> bool: try: with Image.open(image_path) as img: - return img.width >= 800 + return img.width >= 800 and img.width < 1080 except Exception as e: print(f"Error opening image {image_path}: {e}") return False @@ -137,6 +150,9 @@ class WebtoonConverter: def is_new_episode(self, episode_path: Path) -> bool: episode_path_network = self.webtoon_path_network / episode_path.name return not episode_path_network.exists() + + def is_not_empty(self, episode_path: Path) -> bool: + return any(episode_path.iterdir()) def concat_images(self, episode_path: Path): @@ -153,7 +169,7 @@ class WebtoonConverter: with open(img_path, 'rb') as img_file: img = Image.open(img_file) img.load() - if total_height + img.height > 28800: + if total_height + img.height > 20000: self.save_concatenated_image(result_images, episode_path_network, result_index) result_index += 1 result_images = [] diff --git a/data/__pycache__/__init__.cpython-311.pyc b/data/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00056d4a30a8dc291e41835cc4a3aced5b40555b GIT binary patch literal 147 zcmZ3^%ge<81VPif(m?cM5CH>>P{wCAAY(d13PUi1CZpd&ryk0@&FAkgB{FKt1RJ$Tppf-@f#r#0x R12ZEd;|B&9QN#=s0|2{&Aff;O literal 0 HcmV?d00001 diff --git a/data/__pycache__/bomtoon_request.cpython-311.pyc b/data/__pycache__/bomtoon_request.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6ade9986307a71b283b295e5b57a1eef9fa38af GIT binary patch literal 861 zcmZWo-A)rh6y7aT+R~x{2@wbx*QnI&Pb*N7f{3V)KpSEqO-z$!yK}l-+3u9tDV4eM z!guh-d*h4v44Wn<8egCZcU(EU6eMwybLKnW%y;Hw&dj$=CMAjS@b^#qM@*9bgu&I2 zhu}Odz!wQipQZN_mMc;=W?k(G=ZvrsVdqFVu}{V^Jc4iGQ5?r(IDwNmg~xFkXTGL| zcA|VC8+^v*PDe!;ny$$}MGdMV?la64-qH@MB4`y)HgfuN6@eF_RTVvnzzUCdH}WcS z_~RC19e>R*dcB_BAZOc>u;nnS~<6Rad)H9f2pPdAiRMMcVX zi;}j}ZAVQ0X(8lx2(ev2(GpxQ@YrUKCzYz>Vbb%J{;1P7ZQx@b(JVrJp0X(Mea&|m za8e@Nab43`(Q{~VC_&{zl+Wq8A`(Plxrja%ma}N9({Z5+jXj4MD@)Jxr2<;q+dVAp ztH^bZ0quZwOtQmb3{jyjQi!Y-vuRRiD9NV#@h!`O4&%2kXYGw=5$t&OHL-7cc2`u~ zPaIH(P=}rM$KF8`C{UiPfTeA>v?G(>xrSPaG#nTBbgD^cn@@z?TVPg8dksFj z8J7r#n{g$)kkpY6RNJz_W8Wp32w#dY=|Y3G+5sgWz+!%_bSCa0Y+wzW%&ZN&S*LK) z6=&$3v;LF~tbWC}z{Eg(IxZ50@6=xv?vwOuaypn>4(2w4yYs=+dN5rL=25Va4QB5L zvk!vVM}wF&vl|=S%cSE2>C%nHX;O&GSvkrU6&Nuz{2CXD^)|s>7hcft8$@R<2rw9t PWqA;rkVT&VU8MgDvBmVS literal 0 HcmV?d00001 diff --git a/data/__pycache__/kakao_cookie.cpython-311.pyc b/data/__pycache__/kakao_cookie.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bfb78beed0aec2f2e0a74c79d097c689acb27667 GIT binary patch literal 738 zcmZWmO>fgc5S{fpY3wGX;!xBZNL&J?YCwbtRh6oyZLE|Q%9pSrtsJjYLi|D34v-#@ zBlWF#~s&eXyUAw8MnDsornVr#$pTFdCDsY=WI@(>t0KY^q zvD7&^t#fh+6ezO6K{`S}C0(+R1RFr{0Z_Rw0Sve&*5|LmBmf;tU}2h~X*%^S)8|tL z+!NDfsyu~)`dfqr6e-jQ4AVAz-xf93yWi(9m`QDdlh)G(9v^}QI-;OU&-n;Trf7+0 zVY`DP|zs(RL@`OYxA&y5ow%}Pp_Bw`r zeuMphMR`ID*YyG;XnC$r$X7TO+nwFA9@ZZ-?~P>!e*IDRrPT;L&#jA(s<(}{;Sn>Q zzwoxpgt{@LIq`3E2>&_9YA)15t*4E$`LGt&dbN>~4Xa_b$33jF{G>aQTfysAbhYiV zPN{sq(KL->`N3kbMEB=Qp3_|a@cv%sZrfjJ=oQ`Bd{y?>{U_$e%Fh0tS+v>CyF1NR z!`}T^z)UQ{rUYa-2tlSVeuw2Vp?svQZfa{=&xlrucjtm`Z`ZUnc15(sOU-b0mRU;V zTV}I7MBIIs!x$rkMsQ3qOYt`iBYoB~T-KDInVjBh0C&#I0}~@}Y|e`|cv_f9yl(8T`Gn z;=_R@lnlOMVv@6)bMu#G^ULC;8M)TNeQ`Bm?_(g_h@ce?Bk0TBMJs-0cO`)RfI%Dp z4B;Ty4B62+X-(2ElUwV=q_V%kU=TQy_cYfYZBT6@aep+l$$?a#>!uH+x)lt%oAs6t zVRaza+hV^3HXov&0?=pQFW?cFS0Hcs=N2<>xy&2ZJsC8ZO}klE6{1m5!FDrMN-$%o zRJMbfuF1p>N#!zVl72g25_Q#JF)&&HOx9NrgtsTi`D=@Yev`=5ytc4$mE54Zu3f_t zm9DKZH;9JkhfI0H4vV6qDO41#&PEqy_N&>1%?W@l^eUdX7xO=XEj;J zidL|?3zkw&37D*^5+y0&hLI91lj3^Ic}Xj*O9f*4)^v-{Npg{-bGk+iUA4lRo|ajS z9m?tYnnJ8zS|lYhi6vugLJ-%=qO7YbL{m2=tUCCI-CTM>zoDqa@_jyPwZE%f z1h=f}M#*YUTvarz-!>CgD^p(}R4%4xV29fwQ#KUve?$F(j)~R8+@l+2qhd*+#EP&9 znrV~qCT8qnS+*iqOk$*`3UGWZeL=SrRh6=5GNZysZD1k4ER2n2MkfTQ7(YEB+!;Te z6s92N1P;oaLbGR1eUdpfE{x25xx6r+5>#c42p5UGrY8k==_Ct|$bbp3x+JYihEnU2 zohHV^i0|K7ahe&AAilHGHTV4V?-THX=TF&t zJ1tBdMST1HQx4B~tK0OX6JtUf2={L~M;LEMd{?FWl+(e4qloXR#J_Pmnb3v!p+^Cy zoADmR4?jBL#2N2J{J>7p>0`W~am5*6e2{U~8Dd-jtM^T3nDGSSAGkHx1;_q-;{e-e6Fq|W;j+m+hTHU6{X%AUi^5q=h+Vnm}8d816M z!S~Pmgj|#ilhDa4%NNpTA*cf|gwY3ZAewC~R%P9!^-vBR3T2~uh#43L*h1A<$KSDI zf5nbH@xO?rUdB?-TPt6DTggfW)bO$p+x^Mf?vLzmbOj%9TZ`*9X@l9adJcQc(IR>zWUbpvGk0z>}@gG-S z#rt=bcdx&Qr(VWW)t-TBPycR_rx0P+rS0pHuGzMt@_XL1%lI@gBmc}5rmo4;W!t(0Sy{3 z6lM($gEu4_Ef0D92+P#o{S7xi%Lwjgfj1lWJ@syeZ^NH@OZ&{ofP!s#^AdpL=Qys4 Yj#UmmPT&{^6F3b2vbz61pm2x!A9}lvwg3PC literal 0 HcmV?d00001 diff --git a/data/__pycache__/path_constant.cpython-311.pyc b/data/__pycache__/path_constant.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e1e7dfc153293c421b23abe25d1d66044af87bc4 GIT binary patch literal 891 zcmZ8f!B5&y7=NV*C{Qh{PU5CF7P1XUmSu@smJlI^C}3zEj?{FO_cEE#vbM&7J9u=- z4w#sXqo>Vf|HS@)9r_Xu<{cK3opv+v=E?R6B=EKU<^6uY@B7~O+WrWKr+^bF-{<## z836vdWDsnBaed<~z5xgzQwI&E!Z5%C5Cc8mor+)1a~F_p2LY&fsOKH?CS0$d!O{%h zXh!8u9q;L=xG2CxTLYZQ@uwG1OluWKff`zP9E|%6P;iYVT zLmBb;AzROhk|J$MWNtKTsB`H;rc}&jl(Z;jCGjV?cHi91W3(&}hL-cK{?sy5Emd|0 zQ>waVTB>Fxnys6_tA-Bu>*%#H@6@_~m-)wmE0$rH3ovhw-d8>_BOF_u?3cr@huyb$ zI(8Jg;3CJc^AdAQJ#Ojj5$59OIm|sj3R1(q_Y`x99+z;qFyF~^XHV8n*Ul1HNZEkD zq|yT{#Ck&Pd=U#T9PuLhuq)V$NgPe}qAB~?Dvqw&VD6Hi?Fb!ovT(Z4ZDT%eKUu;2 k%5NR>oA%Z==C|#42=mC^)iAHwA6uAj9dYhu%sB_T1tHM;#{d8T literal 0 HcmV?d00001 diff --git a/data/__pycache__/special_list.cpython-311.pyc b/data/__pycache__/special_list.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae5994ff07c24d3cf2dcf0e7f1c4570a5202006d GIT binary patch literal 2462 zcmZuy+fN%;8Xp^mn@K`KXl@M|a%oDErY0l{RI5lK%gbiDXp=T7n-!uA3N$1{20wJ4 z2C%WgcCm|ZfK5!mfWZ)Bo7i0QFZi+Y(@N_za=#zIS{1OJiMLZ{H;;o{cpcT&*?TTK4R?>Ns3Ob)A z0So9tz#_UBu!L3tmeR)nkJBdr%jlDUr|5FP({u&kC-fP>v-CN@^YjJ4Pw9(*mGmV* zimn2@OkV-KN?+?$_!aF+`m-KIdmeoqFu#{*CwmC`^La&k0bNbs=ux&8f_@Y9B3LV? zZ#^cezo;(P765gAD#p7vv?oAhNA#)v_na?^=4aE4{y?@FZJcE}ZCF7T0~ejvmczb! z5coAa8*!lcDjKz<4a?e7U`K&zw6w!BQ@oSm>~C1z#KD>WYyL+~{mp+Kl*%FJkUzp3 zEU^FJ0vOVP#k9@E+PBi{-{&I91bLT-b?&D2eB88- z)y;e?Yv~?*(fP8IYSX^z>Zz)#(pF*)>&En23__*iJL#pL(4-%}_=Ix{(vY)9_IIf` z12z;lv!0oQ3e0A242Yq9GiGD7qJ;oEW;u{^^@LJ!6MFMwdeM{~n&r*99KGNo>olac zOlZxT^QsRZ#>8#yvSS}o+uK^~6+Cgn+mHqr{)dCr3{Ais_TI93lLil&7S~W1c2imR zt~hHImdupkGYa8l(WTcY@rEw|vW{pbvMfYw;&4zHw^M?}E&6n#S4W9UOm=tykP=pQ zvQDF5TF{<_6Z}eyU!6ent4P0zEHUJH18qp{k7_T%qUg|zt~Jr?rbL%rum?mhLy7Zy zqSq{Zu;wZ{VPT{DP|NgK7)`9Oo-uZ9C$&AtZCcR=BS!!yf}WAt*sPQgHd5kn2*PXf zG3#m^X4WtGqk?~z5<>RO2fGl73KKeE&P!z-?o8Mxj&BMk%veL>BY0wjU{o+|W#XZ% z+a?>>wMEYH9{CNa4?gWpykj^@3B$`!E1 zVQEH)>{6l;cW6|uv7E9zV4Q9aU(U%PWQ#x+*aeK<6-?9;5@IkND?7Nt5D#}%J{_@9 znT@!xxSL}zJfDh>r{Wf5*ye{OQD}qpj6j32Hs1DLc4B3UND+wGfZRF;md^0 z;*NeX1=RJcOTo_FeKudK=vf^n|F z7B;pv;}FTxL!skIQ*&E@_ys5sA8&Mj^{h?P|9yAY;J~xyAD(u9H`w3*>KWZR*!gVW zb$3^9XJ1EO@4(>g*FStL`t!?vTHDusUsD4|74SRI2Pow!6beas`kYcCjvzr)NVf^H zQp!`FRwjrtsi^#f^5^`EiA!oJ55#}x*Cy_^NF<0-0YP3(Tx*gFu~I~kmwd;iVl0;s zq&iX4BB`)aN|5E&7U>w4j}zpbMC~`y39OV6ZVX zs=&%8;5R|lNoTNfmLP8=Zv0g`hn4dLc{y?AE9nAOK85p%v$v#+SgFJzo25%wrU>$S z;`1h{3M-ch@^<3Oze!iHay1w78kPwFMkU=MNEKwRQZj`&iQnZSg={J)Qo+_EfIUD? e^>coSMCRp=oW$!#0PCMA%9Kng{?U91RsRj4;a)=k literal 0 HcmV?d00001 diff --git a/data/__pycache__/webtoon_request.cpython-311.pyc b/data/__pycache__/webtoon_request.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7e97ec41640561c35596ce76d3ca1d1af1ddd243 GIT binary patch literal 1531 zcmZ`(TTdHD7#%NOFjsRomxL?}QGs}ET!;f9X=-wm6rzjGcMv zL!~}diqwbj(1*TMRsRi-m8D9N`UghJ8&CaaZG%(VwdVMIXTJI7%tiBie}4~x@23xc z+22JG`dbFAFVZ9*`+&Sh82y00ML3d2i;C6Ck^e9bbwK-g3h*8|kb{+I+n>C>ZNN^xl{YMT5>nuJX}t#C!LaM zleBs26q>Y3j>|2z?Aghs^ir6*_DR{mu|0wfQnQMtXOoT7hsP^n+C3g`7&4xE7R8Qd z^C6o%<)ns5!8I94YRCR%&Nd9LfB`;tLXEAx>U{dD<5|UZjjxFx zz!4bRPQdu#bvvnL`TH+Zpl6rY*Hf95|M%CP!w0XX-l4~iOStlAnaA5zOkA$$Iv>6g zU&475s1MtwA2=SS)j;P{*SlK_;W0gPH?-Vd`G8iltBt*SMwnGJo{)gmW0+c%67g7j z&kn_;9`oR`*>Dlsd-4t=^{7X^Pt^SY&RThtsYf!L?ojTyu9*f0+CtN=w);xUz#eXB zAY@lJv|4s`QOm*gCkNzs%VFueEB8_>S#4qK#jBm|q~0rId~&~R`z9vFS6)D@icgF^@}>f-3wV#TX5?&G;2+5EmahObbYYPDhcA8l znpiA2+<6NoQA7tsAEIODi6x-|6`e>OKTkXmT{4Ivb>KX{Ai8DHgVe$Ep}gpoK_61b-r1sG z@&TkyoQ;dPsMF(&yqQYX*uh=k-5NS*z4NlZ#U z)ySEa{Dz$4wYVwyOoQK&T$8*YW+m4p$6`+Md8FSK3L1H$h|gq<+CWu&`9kk5M#B9F ow>VV%UXU+h4@wkQT=IqA521XqgBbwP5s5@ZbSwhJu0uHfALDhmhyVZp literal 0 HcmV?d00001 diff --git a/data/kakao_cookie.py b/data/kakao_cookie.py index 0cc2e4c..cb2b0f3 100644 --- a/data/kakao_cookie.py +++ b/data/kakao_cookie.py @@ -1,6 +1,7 @@ from dataclasses import dataclass -CLIENT_ID = 2155768539 +# CLIENT_ID = 2155768539 +CLIENT_ID = 3437754656 @dataclass class Cookie: @@ -8,18 +9,17 @@ class Cookie: userID: str ant: str -COOKIES = [ - Cookie(name="ithi", userID="twnu7577d258564215", ant="MFivJ2uk0eyBd7G28D0_4WSk3QXdpHXxp1rkDaNXdCU~"),#ok - Cookie(name="ym", userID="twnu18c780bce30104", ant=""), - Cookie(name="83", userID="twnud41942de09830d", ant=""), - Cookie(name="bjl", userID="twnuf8429dee79c3d3", ant=""), #ok - Cookie(name="yy", userID="twnucbb3bdfce95b85", ant=""), - Cookie(name="hk", userID="twnuf622dd45e496ea", ant="ypc2JaDoKwfgghdheiFRCJvBjWid78M9djJooqOeMnY~"), - Cookie(name="aa", userID="twnuc0728a46c25738", ant=""), #ok - Cookie(name="bb", userID="twnu407ef7f1a046fd", ant="pSQPuFHTEVSztUuDcP4eboMqyY5La0Hb5JRWYILj1z8~"), - Cookie(name="wn", userID="twnu7322f207fb75ab", ant="4q3ArCVX_yx5fTq0kWWCanc60SXEnUU3QyuF0wys8Hc~") -] +COOKIE = Cookie(name="ithi", userID="koru685bfca187016d", ant="6omfFxw3u2ksAbZDZmLY8sFsQcGARzqc1lrRv-fiblg~") -COOKIE_NAME = 'ithi' -URL_TYPE = '1' # 1, 3, 7, m, p -TASK_TYPE = 'dc' # d, c \ No newline at end of file +# search Network -> JS -> _app-... -> open in Sources panel +# case 0: +# if (r = t.userId, +# o = t.episodeId, +# i = t.timestamp, +# s = t.nonce, +# u = t.aid, +# l = t.zid, +# window.crypto && window.crypto.subtle) { +# e.next = 4; +# break +# } diff --git a/data/kakao_request.py b/data/kakao_request.py index adbaa6f..c91acef 100644 --- a/data/kakao_request.py +++ b/data/kakao_request.py @@ -10,20 +10,20 @@ class KakaoRequest: def get_episode_headers(self, ant): return { "Accept": "application/json, text/plain, */*", - "Accept-Encoding": "gzip, deflate, br", - "Accept-Language": "zht", + "Accept-Encoding": "gzip, deflate, br, zstd", + "Accept-Language": "ko", "Cache-Control": "no-cache", "Cookie": f"theme=dark; _kp_collector={self.app_id}; atn={ant}", "Dnt": "1", - "Origin": "https://tw.kakaowebtoon.com", + "Origin": "https://webtoon.kakao.com/", "Pragma": "no-cache", - "Referer": "https://tw.kakaowebtoon.com/", + "Referer": "https://webtoon.kakao.com/", "Sec-Ch-Ua": '"Not A(Brand";v="99", "Microsoft Edge";v="121", "Chromium";v="121"', "Sec-Ch-Ua-Mobile": "?0", "Sec-Ch-Ua-Platform": '"Windows"', - "Sec-Fetch-Dest": "empty", - "Sec-Fetch-Mode": "cors", - "Sec-Fetch-Site": "same-site", + "Sec-Fetch-Dest": "script", + "Sec-Fetch-Mode": "no-cors", + "Sec-Fetch-Site": "cross-site", "Sec-Gpc": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", } diff --git a/data/path_constant.py b/data/path_constant.py index b52c408..0e8c9cc 100644 --- a/data/path_constant.py +++ b/data/path_constant.py @@ -3,6 +3,7 @@ from pathlib import Path DOWNLOAD_DIR = Path('E:/') / 'Webtoon' NETWORK_DIR = Path('//TRUENAS') / 'Media' / 'Webtoon' +NETWORK_DIR = Path(r'\\TRUENAS\Media\Webtoon') TEMP_DOWNLOAD_DIR = Path('E:/') / 'Temp_Webtoon' DOWNLOAD_LIST_TXT = Path(DOWNLOAD_DIR) / 'download.txt' diff --git a/data/special_list.py b/data/special_list.py index 4cefc8c..0264f47 100644 --- a/data/special_list.py +++ b/data/special_list.py @@ -1,62 +1,94 @@ WEBTOON_NOT_PROCESSED = [ '陷阱', # 完结 + '情侶破壞者', # 完结 + '我獨自升級', # 完结 '8級魔法師再臨', # 完结 + '婚姻這門生意', # 完结 '婚姻這門生意[18+]', # 完结 - '守護女主角哥哥的方法', # KakaoTW完结 - '轉生後變成天才', # KakaoTW完结 - '兩個繼承人', # KakaoTW完结 - '患上不出道就會死的病', # KakaoTW完结 - '無法品味的男人', # KakaoTW完结 - '摘下偽善男主角的面具', # KakaoTW完结 - '皇家婚姻', # KakaoTW完结 - '鐵血家族獵犬的重生', # KakaoTW完结 - '重生百次的最強玩家', # KakaoTW完结 - '我獨自升級', # KakaoTW完结 - '結局創造者', # 停更 - '黑影之夜', # 季休 - '狂魔重生記', # 季休 - '在魔法學院偽裝教師', # 季休 - '兔子與黑豹的共生關係', # 付费 / 季休 - '成為家人的方法', # YDS下载后续 + '惡女重生', # 本篇完结 2394 + '試著改變故事類型吧', # 本篇完结 2494 + '皇家婚姻', # 本篇完结 2952 + '關於你的愛', # 完结 + 'Netkama Punch!!!', # 完结 + '守護女主角哥哥的方法', # 腰斩 + '唯一的希望', # 季休 + '轉生後變成天才', # 季休 + "地下城見聞錄", # 季休 + "符文之子", # 季休 4129 + '兩個繼承人', # KakaoTW完结 두 명의 상속인 + '成為家人的方法', # 在kakaopage连载 가족이 되는 방법 + '同情的形態', # 동정의 형태 ] WEBTOON_18_BONUS = [ '婚姻這門生意[18+]' ] -KAKAO_ONLY_MAIN_ACCOUNT = [ - '152', # 骷髏士兵卷土重來 - '167', # 試著改變故事類型吧 P - '222', # 成為我筆下男主角的妻子 - '247', # 領主夫人罷工中 - '322', # 婚姻這門生意 P - '330', # 同情的形態 P - '399', # 噬魔法師 - '424', # 地下城見聞錄 - '587', # Pickmeup - '591', # 武當奇俠 - '736', # Boss大人請振作 - '784', # 永遠的謊言 - '787', # 魔法師的校園生存法則 - '862', # 符文之子 -] +KAKAO_TO_TW = { + "나 혼자만 레벨업": "我獨自升級", + "해골병사는 던전을 지키지 못했다":"骷髏士兵卷土重來", + "악역의 엔딩은 죽음뿐": "反派角色只有死亡結局", + "악녀는 두 번 산다": "惡女重生", + "장르를 바꿔보도록 하겠습니다": "試著改變故事類型吧", + "무당기협": "武當奇俠", + "내 남자 주인공의 아내가 되었다": "成為我筆下男主角的妻子", + "빌어먹을 환생":"轉生後變成天才", + "로열 메리지": "皇家婚姻", + "상냥한 남자주인공의 가면을 벗기면":"摘下偽善男主角的面具", + "맛볼 수 없는 남자": "無法品味的男人", + "데뷔 못 하면 죽는 병 걸림":"患上不出道就會死的病", + "여보, 나 파업할게요": "領主夫人罷工中", + "던전 견문록": "地下城見聞錄", + "픽 미 업!": "Pick me up!", + "철혈검가 사냥개의 회귀": "鐵血家族獵犬的重生", + "만렙 플레이어의 100번째 회귀": "重生百次的最強玩家", + "룬의 아이들": "符文之子" +} -KAKAO_1 = [ - '41' -] +KAKAO_1 = { + "2358", # 骷髏士兵卷土重來 +} -KAKAO_3 = [ - '303', # 天才詐欺犯的雙重身分 -] +KAKAO_3 = { + "2830", # 成為我筆下男主角的妻子 +} -KAKAO_7 = [ - '41', # 反派角色只有死亡結局 - '116', # 惡女重生 - '200', # 暴君就該配惡女 - '233', # 少女賭神愛黛兒 -] +KAKAO_C = { +} -KAKAO_PAY = [ - '230', # 兔子與黑豹的共生關係 - '516', # 結局創造者 -] \ No newline at end of file +KAKAO_W = { + "2383", # 反派角色只有死亡結局 + "2499", # 武當奇俠 + "2977", # 摘下偽善男主角的面具 + "3008", # 患上不出道就會死的病 + "3205", # Pick me up! + "3455", # 鐵血家族獵犬的重生 + "3786", # 重生百次的最強玩家 +} + +KAKAO_P = { + "2998", # 無法品味的男人 + "3024", # 領主夫人罷工中 +} + +BOMTOON = { + "Netkama Punch!!!", # done + "關於你的愛", # done + "PAYBACK", # every 10 days + "披薩外送員與黃金宮", # every 10 days + "No Moral", # every 10 days + "1995青春報告", # weekly + "Backlight", # weekly + "Unsleep", # weekly + "鄰居是公會成員", # weekly + "鬼夜曲", # weekly + "監禁倉庫", # pending + "棋子的世界", # pending + "易地思之", # pending + "夢龍傳", # pending + "融冰曲線", # pending + +} + +BOMTOON_TEMP = { +} \ No newline at end of file diff --git a/data/webtoon_request.py b/data/webtoon_request.py index 8eaf650..5defbd1 100644 --- a/data/webtoon_request.py +++ b/data/webtoon_request.py @@ -21,19 +21,3 @@ def get_webtoon_headers(): "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", } - -def get_bomtoon_headers(): - return { - "Accept": "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8", - "Accept-Encoding": "gzip, deflate, br, zstd", - "Accept-Language": "de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6,zh;q=0.5", - "Priority": "i", - "Referer": "https://www.bomtoon.tw/", - "Sec-Ch-Ua": '"Not_A Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"', - "Sec-Ch-Ua-Mobile": "?0", - "Sec-Ch-Ua-Platform": '"Windows"', - "Sec-Fetch-Dest": "image", - "Sec-Fetch-Mode": "no-cors", - "Sec-Fetch-Site": "cross-site", - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", - } diff --git a/downloaders/__pycache__/__init__.cpython-311.pyc b/downloaders/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0bb5ce7812cafde72ec4c1d586605cbd54ca2669 GIT binary patch literal 154 zcmZ3^%ge<81olncX(0MBh=2h`DC095kTIPhg&~+hlhJP_LlF~@{~09tOE=XjCZH%k zD>b>KIL5a!JT<8#KR+)fCBHl`CqFSIwWv5IK0Y%qvm`!Vub}c5hfQvNN@-52T@fo# YH^}^AejxFInURt40|SgGVg`x<0O+J6ivR!s literal 0 HcmV?d00001 diff --git a/downloaders/__pycache__/bomtoon.cpython-311.pyc b/downloaders/__pycache__/bomtoon.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72ada3435f2a74ad394511a5c2acd4b3b990b4f5 GIT binary patch literal 3593 zcma(UU2hx5agTRL9zTw>M9H#cQKmm&l(8hEk(FACZOM`B$aNz-Rnb6%;RD4zN>q*? z**nG-K?4G6pbX##!AK#&ZDAn^90fK|z10u?0evI|0wxX!AfN~kzp+ps3O{w`_@%Su z)_cp@nVsF)+1Z)d<@d7eAyDo<-$>kY5%NzYnni3NyLmv?2ql!$NQ#Sc9O4emk>aDg zh4Y$_5~HGp3z{?P1iqlTqAqAfO-i|=Zj0yCJSlI~YvC@flx zPskJa)l1Y*g;^5uyhJj>nZk{ju?VgFySW>emC5PLlh>}!e2g6*sj-}qOysoLOfEYE zJ-(^ypUwQ{`lTsl>gsQS4lbyMGM`BqnM_()RAW?SI&^!cGPl!O2FZA1QLB*}#BLOj zKf>l!66Gj~I*KI0QSP1;<*DNyi3*elC{jUns;?xb*18t)@(x10{Gj_0{9cRMhvdaeGKT67P+y9)9NGz?lJT~ zY4JE>G*1%-*(DT+(y}-uv*ZVECL)-kp3ADtloTbIP8y2Bz&nWPYMPEW1zpt=rsuXg zZ`m|SO}C=NwV19eijD%q;gwed>e$U2EVHD>4gKaP%b(Q;-=r<8>o*&RGL&6rewdX% z&@jkQb7dKN^#K6?Ss_14@>j0=t{2jwZRyZ@Z&^B7kxrJRlRx#mSMC|8^b8b*`@SN4 zyD*;EY>NB0Z2AE}yG55;b}DcMveY_>r`%(}pS0+^j)G&UZ7av;7H5@XQNxYv)eDSi z*_!gNN#G0ohO;?xZ%YOHiQN?n0-Gub8))}8iv0vPn(k^FM@73t>TEmc#vXm!lk8H# zFRa{v{I_Ziu1(!Z?y2XseToH<^%lhay_0(Fn!!rmrWXHWB_EadS;<*&!b;9}Udi_k zD@oM9&q}U>i+xgXz1vD}Ig@~W=XWJ~pa5y2eKKDX=7*k+b`MwwL%=F&**u zvN5JZrVx#!p{cL9L55Lb3MtiynL;|2QcWi$=fwO+AyUo%Rrhj zmh=A1!WdQcI7?=YWF~D&pR3EaGmPr5xPcr-%*&N;e}4YeOAJ+$_R@6P>uK^ zWT-@jrD8a5EF9@HC8mCnQ*}c(c}V+~!lqX@Vn$9^;u*;G7@sU;shLQosVU*@V5oNt zNPAf=iH!k@upS(D1jl*z>c$(f1yeQ_bE)}sEU76urm+qj{8yB-InKmMBhK4tJ z7-#ZK)ggDnHPx2R-b1zkQgzdfP|=d0J;rFbSy&LqbSiJ`IN#e!m~ZepVUE5ECWFlB zKX_kTAKnz6`bz$hvVWxFA1U%XUjO~^_4u~;SlN5*$6$CP_}!7sR5^IQ54lWYX8Yc zOAnVe510FgEB(VT$-C3r|0wb>vT_0OV9(mS#GQQ3+1KegeT75#@nuU-;4V z!|9E=a^IOs-)HVV zaqD_QU&qJREMb_cet?IMm8pE17#NxOKjtdOeY zD+-o2rjw~gj-_pv=G(MlAHeL0qwd#odkL!E(J}nP4MlqtjI0(v61&F}!V8d{!7eFjNlRyD;sVgkf*St;e!F;gT?4c3v1MIr!P1&t0KO%O zT?9-=85nDI0|%7m6GgF>$@jGMwKGsf9n z#C?kLMJ}e*Io6|xodpT(R{%@_L*L{f|FHAei9TWSkVV)8Vshh+6<0M)QNT5<5XVhV zSei53wgu36^uGasXH)a5Ie2lfM)m=oKJmjEX@QHJC_{iXfdtqy;o0nS_nyu++vybL zorAqK5!#xQ_&aJY#3T|tSaT!hAs&Ctix~O8z&dbi2a$m3!t>UW^K~;gjpb}Iy}H**=v=AO7>bMz7^}M qlE8}fRmtg+y;jNDlD$^RV98!<&i6Sm@|&Qz^e^oF_dYCZWB&!j{!DuS literal 0 HcmV?d00001 diff --git a/downloaders/__pycache__/decrypt.cpython-311.pyc b/downloaders/__pycache__/decrypt.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6d437c45d0e3407296b70a1156be412fea662cd7 GIT binary patch literal 3208 zcmcH*OKclOboS%5oyJaMH*wl3t&_AW7LY>e2SHkBk_uI&p(2zjCUoWOPLgfyT{F9G z>eQ(s6eLq36`@v%#78-x;Hn&Q04WC!95`@7cC}Ki6_Jn-2ROMZhpH#u?AmKPArcp6 zci)>g@4eZXH}5mw1OhDx+9%_yQ@?r;`kfNB##cL!2cWZrWF#|56n8KaXMkptY>JC> z3}Vq6Nahxh%&)Qt-G|R1D}9}NuTUp;5W&6f#buJ%kFlyCt2CIz{mZv zAJ*1ld55t?vS~x|x)KO>gb)Yc<2ZmNlw)#8blEu;D5gf{IH24u=Xju~rrmNLplXIh zIRPjzv(scWitaHZF~Q=+gd$rlB2Fv1CgZcTZzw6O8;Mlf5=2!~C2V=cjE)H~d2v>e zV_u8badOh)sII@R>aS~h97Un3$AOwoHyL%>)93Ij;}?i_1xtoLem;8%PZ*k}j?3DN zn$!|9Ci=MS*rq?7wS1zesEQ$qvpcI6>#vQ_N%a>1%%k$INSpuy#FyZh2)wN2mwCZ@V`K32^kGM!!|~Hp^HLa5W<1|)ftV-GFp>)>v zN=?>M`1+y#=!GOs=s2pV;1$VaR57B47KM|I8qy&RBe>v9MkNdqlnND zjqzk3Ad6G+jO9E3*6aD9ErxsIruH3$BDe_rlIlL@=kt__B+7FxUhx0gC1 zTQ`^GQd^(d)>riPkq9U^B?s5MQE<-YvQxax8i1F_?6q5 zkEhv`E7Ss8TC>I+)0noJR_4~2Eo))_pS3_5WIk#resiqRUSIwdH>rW0?5hCfHSc(i z+g4*oZKh2cPy<(5YrIAbwUWuP&2GG9g-|oM(dPeywg5F3$~~l8cI3~U@bg%}@=ho8 z>7+7Y3Ho&6$g$&uE{@Q>6S`?jfRqb`*Air93aT!l`?7=y$hXIjTK#T@MI}GZHA}wzKziCJN+xeU!N=X46g0^Za*M+ANHJm&~vubbI$BJ zR|>sphTeP_8hsENErl+cp^L?L$BLK60GgqA-n-GY&+K}puu7)m>LrNxeJ%x>FO?EP&XCiB0-9AmrV`S1vOpbx%J!DUAp9!HU!Ui<*^{)WT zyHAA`JY5y!f}Kccudsekq=EoE;(%Ou&l%{nS)Av<@|g+(RPfkn?U;?$FWRUMTwM^Y zw7_JAjzVS)7jkB7z@B_t;4Gc48m<9{>EfRSV6{k^YT)Yzgn@lIaP~#-_Vv9!^$j@o xJ4H?bgQisdJpfQG8K#T^^Y&9l!Fl^Bqy5F^w;~*1VDDSOBRcl?zcB2d{{xq1%U1vZ literal 0 HcmV?d00001 diff --git a/downloaders/__pycache__/downloader.cpython-311.pyc b/downloaders/__pycache__/downloader.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c9deae77fef3df143f34f5deb305d50c01d5c305 GIT binary patch literal 11016 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 literal 0 HcmV?d00001 diff --git a/downloaders/__pycache__/happymh.cpython-311.pyc b/downloaders/__pycache__/happymh.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..102dcff793bfbf30f00bba2173de134dbfda27af GIT binary patch literal 8433 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 CQ3H?w literal 0 HcmV?d00001 diff --git a/downloaders/__pycache__/kakao_webtoon.cpython-311.pyc b/downloaders/__pycache__/kakao_webtoon.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a69bc0d94f0a8ce4658eb0791ee0f07002d1a5e GIT binary patch literal 10828 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 literal 0 HcmV?d00001 diff --git a/downloaders/bomtoon.py b/downloaders/bomtoon.py deleted file mode 100644 index 92e12de..0000000 --- a/downloaders/bomtoon.py +++ /dev/null @@ -1,62 +0,0 @@ - -from pathlib import Path -from typing import TYPE_CHECKING -from bs4 import BeautifulSoup -import httpx -import requests -from data.path_constant import DOWNLOAD_DIR -from data.webtoon_request import get_bomtoon_headers -from downloaders.downloader import Downloader - - -class Bomtoon(Downloader): - def __init__(self, webtoon_id): - super().__init__(webtoon_id) - - self.headers = get_bomtoon_headers() - - def _fetch_information(self, url): - res = requests.get(url, headers=self.headers) - - if res.status_code == 200: - soup = BeautifulSoup(res.content, 'html.parser') - title = soup.find('title') - if title: - self.title = title.get_text().split('-')[0].strip() - - author = soup.find('meta', attrs={'name': 'author'}) - if author: - self.author = author.get('content') - - description = soup.find('meta', attrs={'property': 'og:description'}) - if description: - self.description = description.get('content') - - tags = soup.find('meta', attrs={'name': 'keywords'}) - if tags: - tags_list = tags.get('content').split(',') - if '連載' in tags_list[0]: - self.tag = tags_list[1] - else: - self.tag = tags_list[0] - - self.thumbnail_url = "" - self.thumbnail_name = self.webtoon_id + '.jpg' - else: - print(f"fetch_information: {res.status_code}") - - - def _fetch_episode_information(self): - pass - - - def _get_episode_image_urls(self, episode_index) -> list[str]: - pass - - async def _download_image( - self, - episode_path: Path, - url: str, - image_no: int - ) -> None: - pass \ No newline at end of file diff --git a/downloaders/decrypt.py b/downloaders/decrypt.py index 2527438..8eae0d3 100644 --- a/downloaders/decrypt.py +++ b/downloaders/decrypt.py @@ -1,8 +1,7 @@ import base64 import hashlib from contextlib import suppress - -from WebtoonScraper.exceptions import MissingOptionalDependencyError +# from Cryptodome.Cipher import AES class Decrypt : def __init__(self, aid, episodeId, timestamp, nonce, userId, zid): @@ -18,7 +17,7 @@ class Decrypt : with suppress(AttributeError): return cls.AES try: - from Cryptodome.Cipher import AES + from Crypto.Cipher import AES except ImportError: raise ImportError("Missing optional dependency 'pycryptodomex'. Please install it to use this functionality.") diff --git a/downloaders/downloader.py b/downloaders/downloader.py index cbdd9ad..117589e 100644 --- a/downloaders/downloader.py +++ b/downloaders/downloader.py @@ -2,7 +2,7 @@ import asyncio import html import json from pathlib import Path -import pyfilename as pf +#import pyfilename as pf import shutil import time from httpx import AsyncClient @@ -53,11 +53,13 @@ class Downloader: if information_path.exists(): with open(information_path, "r", encoding='utf-8') as json_file: existing_information = json.load(json_file) + if (self.author == ""): + save_necessary = False if ( existing_information["title"] == self.title and existing_information["author"] == self.author and existing_information["description"] == self.description and - existing_information["thumbnail_name"] == self.thumbnail_name + existing_information["thumbnail"] == self.thumbnail_name ): save_necessary = False if (save_necessary): @@ -66,7 +68,7 @@ class Downloader: "author": self.author, "tag": self.tag, "description": self.description, - "thumbnail_name": self.thumbnail_name + "thumbnail": self.thumbnail_name } with open(information_path, 'w', encoding='utf-8') as json_file: @@ -91,10 +93,15 @@ class Downloader: def _get_unobtained_episodes(self) -> list[int]: downloaded_episodes = [] - + for dir in self.webtoon_path.glob('*'): if dir.is_dir(): downloaded_episodes.append(int(dir.name.split('.')[0])) + + if self.title == "反派角色只有死亡結局": + downloaded_episodes = [i - 2 for i in downloaded_episodes] + if self.title == "成為我筆下男主角的妻子" or self.title == "領主夫人罷工中": + downloaded_episodes = [i - 1 for i in downloaded_episodes] if self.title in WEBTOON_18_BONUS: count = len(self.readablities_index_list) - len(downloaded_episodes) @@ -114,6 +121,10 @@ class Downloader: for episode_index in episode_index_list: episode_name = self.episode_titles[episode_index] episode_title = self._get_safe_file_name(episode_index, episode_name) + if self.title == "反派角色只有死亡結局": + episode_title = self._get_safe_file_name(episode_index + 2, episode_name) + if self.title == "成為我筆下男主角的妻子" or self.title == "領主夫人罷工中": + episode_title = self._get_safe_file_name(episode_index + 1, episode_name) # episode_title = self._get_safe_file_name(f"{episode_index}.{self.episode_titles[episode_index]}") print(episode_title) episode_path = self.webtoon_path / episode_title @@ -165,4 +176,4 @@ class Downloader: episode_title = f"{episode_index}.{episode_name}" - return pf.convert(html.unescape(episode_title)) \ No newline at end of file + return html.unescape(episode_title) \ No newline at end of file diff --git a/downloaders/kakao_webtoon.py b/downloaders/kakao_webtoon.py index 8025370..1eeea04 100644 --- a/downloaders/kakao_webtoon.py +++ b/downloaders/kakao_webtoon.py @@ -10,6 +10,7 @@ import requests from data.path_constant import DOWNLOAD_DIR, DOWNLOAD_LIST_TXT from data.kakao_cookie import Cookie from data.kakao_request import KakaoRequest +from data.special_list import KAKAO_TO_TW from downloaders.decrypt import Decrypt from downloaders.downloader import Downloader @@ -27,7 +28,7 @@ class KakaoWebtoon(Downloader): self.post_headers = self.kakaoRequest.get_post_headers(self.cookie.ant) def verify_cookie(self) -> bool: - url = f"https://gateway.tw.kakaowebtoon.com/episode/v2/views/content-home/contents/{self.webtoon_id}/episodes?sort=-NO&offset=0&limit=30" + url = f"https://gateway.webtoon.kakao.com/episode/v2/views/content-home/contents/{self.webtoon_id}/episodes?sort=-NO&offset=0&limit=30" res = requests.get(url, headers=self.episode_headers) return res.status_code == 200 @@ -39,15 +40,22 @@ class KakaoWebtoon(Downloader): description = soup.find('meta', attrs={'name': 'description'}) if description: self.description = description.get('content') + self.description = "" thumbnail_url = soup.find('meta', attrs={'property': 'og:image'}) if thumbnail_url: self.thumbnail_url = thumbnail_url.get('content') all_p = soup.find_all('p') - self.title = all_p[0].get_text() - self.author = all_p[1].get_text() - self.tag = all_p[2].get_text() + title = all_p[0].get_text() + if title in KAKAO_TO_TW: + self.title = KAKAO_TO_TW.get(title) + self.author = "" + self.tag = "" + else: + self.title = title + self.author = all_p[1].get_text() + self.tag = all_p[2].get_text() self.thumbnail_name = self.webtoon_id + '.' + self.thumbnail_url.split('.')[-1] def _fetch_episode_information(self): @@ -56,7 +64,7 @@ class KakaoWebtoon(Downloader): is_last: bool = False webtoon_episodes_data = [] while not is_last: - url = f"https://gateway.tw.kakaowebtoon.com/episode/v2/views/content-home/contents/{self.webtoon_id}/episodes?sort=-NO&offset={offset}&limit={limit}" + url = f"https://gateway-kw.kakao.com/episode/v2/views/content-home/contents/{self.webtoon_id}/episodes?sort=-NO&offset={offset}&limit={limit}" res = requests.get(url, headers=self.episode_headers) if res.status_code == 200: json_data = res.json() @@ -88,15 +96,15 @@ class KakaoWebtoon(Downloader): self.episode_ids = episode_ids self.seo_ids = seo_ids self.episode_titles = episode_titles - self.readablities_index_list = [index for index, value in enumerate(readablities) if value == True] - + self.readablities_index_list = [index for index, value in enumerate(readablities) if value == True] + def _get_episode_image_urls(self, episode_index) -> list[tuple[str, bytes, bytes]] | None: episode_id = self.episode_ids[episode_index] - url = f"https://gateway.tw.kakaowebtoon.com/episode/v1/views/viewer/episodes/{episode_id}/media-resources" + url = f"https://gateway-kw.kakao.com/episode/v1/views/viewer/episodes/{episode_id}/media-resources" payload = self.kakaoRequest.get_payload(episode_id) res = requests.post(url, headers=self.post_headers, json=payload) - + data = res.json()["data"] aid = data["media"]["aid"] diff --git a/dungeon_odyssey.py b/dungeon_odyssey.py new file mode 100644 index 0000000..c5429f2 --- /dev/null +++ b/dungeon_odyssey.py @@ -0,0 +1,58 @@ +from pathlib import Path +import json + +from data.path_constant import DOWNLOAD_DIR, NETWORK_DIR, TEMP_DOWNLOAD_DIR + +# DUNGEON_HOME = Path('E:/') / 'Webtoon' / '地下城見聞錄' + +# for i in range (114, 115): +# name = str(i) + '.' + '第' + str(i + 1) + '话' +# path = Path(DUNGEON_HOME) / name +# path.mkdir(parents=True, exist_ok=True) + + +# for first_level_path in TEMP_DOWNLOAD_DIR.iterdir(): +# if first_level_path.is_dir(): +# data_path = first_level_path / 'information.json' + +# with open(data_path, 'r', encoding='utf-8') as file: +# data = json.load(file) + +# # 2. 修改属性名 +# if 'thumbnail_name' in data: +# data['thumbnail'] = data.pop('thumbnail_name') + +# # 3. 保存修改后的 JSON 文件 +# with open(data_path, 'w', encoding='utf-8') as file: +# json.dump(data, file, ensure_ascii=False, indent=4) + +# print("属性名已修改并保存!") + +# for first_level_path in DOWNLOAD_DIR.iterdir(): +# if first_level_path.is_dir(): +# print(first_level_path.name) + + +# HOME = Path('E:/') / 'Webtoon' / '鄰居是公會成員' / '65.第66話' + +# for img_path in HOME.iterdir(): +# print(img_path.name) + +# for file in HOME.glob("*.webp"): # 仅遍历 .webp 文件 +# new_name = f"{file.stem.split(' ')[0]}{file.suffix}" +# # new_name = f"{(int(file.stem) - 1):03d}{file.suffix}" # 转换为 3 位数 +# new_path = file.with_name(new_name) # 生成新路径 +# file.rename(new_path) # 重命名文件 +# print(f"重命名: {file.name} → {new_name}") + +# print("所有文件已重命名完毕!") + +# HOME = Path('E:/') / 'Webtoon' / '鬼夜曲' +HOME = NETWORK_DIR / '鬼夜曲' +for episode in HOME.iterdir(): + if episode.is_dir(): + index = int(episode.name.split(".")[0]) + if index > 65: + new_name = f"{index}.第{index - 3}話" + new_path = HOME / new_name + episode.rename(new_path) \ No newline at end of file diff --git a/helper.py b/helper.py index c221e21..dadeb5e 100644 --- a/helper.py +++ b/helper.py @@ -2,7 +2,7 @@ from pathlib import Path from data.path_constant import DOWNLOAD_DIR, NETWORK_DIR, TEMP_DOWNLOAD_DIR from helper.missing_episode import get_missing_episodes from helper.missing_images import get_missing_images, resize_and_overwrite -from prerequisite import delete_all_empty_episodes +from helper.prerequisite import delete_all_empty_episodes # delete_all_empty_episodes(DOWNLOAD_DIR) # delete_all_empty_episodes(NETWORK_DIR) diff --git a/helper/__pycache__/__init__.cpython-311.pyc b/helper/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b999644f7b7e083d8cdc8d0033203981e85a632 GIT binary patch literal 149 zcmZ3^%ge<81j22-X(0MBh=2h`DC095kTIPhg&~+hlhJP_LlF~@{~09tOC!}PCZH%k zD>b>KIL5a!JT<8#KR+)fBQ>WWwJ0V&J~J<~BtBlRpz;@oO>TZlX-=wL5i3w5$mn8z TAn}2jk&*EO1B@tQ28say?Svsd literal 0 HcmV?d00001 diff --git a/helper/__pycache__/get_kakao_list.cpython-311.pyc b/helper/__pycache__/get_kakao_list.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7bf2ad33dd38b7d0b63bed8e1d866c0bd45c1a16 GIT binary patch literal 962 zcmZ3^%ge<81o8I!(ho5*FgylvV1NnA_^bhBOlL@8NMX!j$YqRT%w>vV%4LpX&Si;W z$z_dVWn@TUYGH_COJNRX&}4ZD(x%CJi{0DN+tEMX5XLZuF`QwH02m`Y8E7{gFaud2 z{MigBI-Mb%p_Va)v4)|Hp-8HRaRFQzGNT0MR-oWApsv+$^FVGRpqB+<5{6!80(#l- z=w%_GmjjPpRswp#u?EqP?&casU@Wlt-C{Jn#b|tsG5Ho_`7Oo*O}1NXsTC!uc_~Gp zuq^_4x`-7-u!9JYKQx(cv1aBKl$PA$%1+Eq%#Y8>EG{Vqg{*?YFTGT&n1G`EtkmR^ z;uzn`@YJM|{QSI_jMSWh)S{U5)RK6F9=(FfTY?Co%oMOmMM6N6Kz0|;0}>4k4+Mo9 zydJP~b)+`bG}PSS5W3DGd5J@EuKGm|r7Ijt7dVt|41nYVZqW-YqMv~-Wbo5uza^NG zSdyq$T#%ZanV19d23qieLJks!ApfmoC=vz<2m*;;95%W6DWy57c12=9E+|PBdjN?K a%#4hTHyAuG5Qjdn@H2{iV8A56HUR(wsLYW7 literal 0 HcmV?d00001 diff --git a/helper/__pycache__/missing_episode.cpython-311.pyc b/helper/__pycache__/missing_episode.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed6831a3e8264042fbe9772670db18a9396c808f GIT binary patch literal 1461 zcma)5L1-IC6rI^!SxOaE>+Z@?YDIODRV^V$P6&x_ zADn|sx?84?m{jL}5$+TCOPIgB0NA<5X*Ylcn*8uz+v1_^e)uL>hYyqnv|w|XtDg^| z$v?}WA@En^_&#QM6U5&H4~h-RlKvO>MN4QN2r^6wEa|a-ubwrfgWd3o;1=(GyMF^D zob0EA83>-w(r7;kEP);$oPHO#cn914E*c>k8)U5zl8)kSjL`T1i3DpT$i$V%Ho|mt zfU&~C&MaXCl7uB5p}`?EzekQLLu_J@4GB+rN|CcPF@%BxC4+bf0=PqLkWb0;1OomT z&h5ei@fG5uC_cffuitXZMOj#`|75#22H)^fd$4a6j)T*WM8)+;(?BW8_IjF_k! zl!ZvySjJ2=DPdv-8!S{OJf4dXzjr1t>BOX^3SPmLl0WV&g6q0jL)c_;RX0uDST6M= zqb6Rg8E6BK*HZ34W z;$fOPiHB&?L^VxXUdEW%Lp5pd+oVZ*n;lt7WU11mX%D;wu}K9@+wOJ#>UEX4w$D93 z-#y1ZzUSPp-0ocfjrTwL`*wu};5RI^hONoG3?k++-~b0RAPeUq#JnwI%M$Yh;)xHz zJjs!WQXF+11nB2@VE`J`R5qF75wm+@=4SlL%=koNV(Qvr)2%t-=mdIRev6_#1~Tjz z({26v=RmPtmC?w@2qg&z^^Eqy-iWYFmNyeV6AW1?lm1xRc z@{%B0knTpT7QmTJ-b)BMj=yI>LBiW&pgCTL`O^!tVm{3;%F@F0{hRy;az3A1SmHAU zURW@2sYTGI(uAWPw4E4I_RRpFECLZOwH9-bq!Tq zLz>I4yZkS}UT3&A9IXzYtqh;lhGY70tPb|L5grk>Yp~|=S3Sono@1J4ME8u;fxA0g z^BpQ(tPQ_aiq}GCtD(tCXi^JZ(nFW(0QStn2347(HKzXy_h;@e7uDctJs7J7Co93p z?-#Y;Wj%QL=TSX4tsRN$N8%bYqcbzAd2jfGPfvX(ss1s|Kc@S~O4Azyhs)FJL%Khz z4V=;kPL-xMd_(2YvY`2n>%QZ4VC#vRWDVB(*z(1-*jlW{2CM97g&kcV)Yyp5MpQPk zx#8_yJEwVrx;OarYQ-B?z2ObUr}l^S{*xLL(V2+KL`==wdVfS?qB;{*nJCh%?R9V%i~@;VvE3u)zzRCi<^V*OS<|+-LEfA^KO1m}I9UdnN9AgelJI~R z-B>}3JQj>y*LW$JMU|UN8TKpV@eA`a*AXtc%m?hoE+JRAD>n*tx11DlQLG3YDqBLx zrVOgV(@VUNT9OTS6VVi1jR+j7&Xk0|wpoiJi{tNaSQAN9`}t*FToG}L2_qBk(9$o6 zJOsZx_dMz?UDlk3bmyTua8V=WlfN>3pF2KwuTrbaf70L}ySDIjQuB?j-_Y1GjX9$; zXI9;H8hCpjr$0%TL+fsJ@Z3+7{_d>)?t9vSIsL%gstehj`>PJ7;$YT#%QrMfNOy!( zN2u;aA&kpBV(p&)bs literal 0 HcmV?d00001 diff --git a/helper/__pycache__/prerequisite.cpython-311.pyc b/helper/__pycache__/prerequisite.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6028b6a7b5265d73f491b549216c6db8cfc4f6f GIT binary patch literal 4903 zcmbtX-A^0Y6~8m%&lm?2&$1*CGfkkxO)!BF8Vm^`HJe2tCJE#Nm&o#Z25@RT-aEq* zh^cr;rLDvRs-h;FWEEOf*{;$DULy6O51ai1su?;G)>KH9s!H9b^(w7IdFr`iY&_U$ zt@h5HbI;s4_sqR_&hMOazi_(`ASm?%ndwFsLVqO>g+lK#Z)bpcjCjOTNfaS1oup>z z2uIcq&6bL!Z-T84AhHf&@bMieJo>%a!VaH9Ujg`#G~; z+sel)&v^vD%o5&VLqbxJ6RByAPhcS~i+CZ(E!GXqrsajbF0O6o{Wgv#q5{uJbMd$! zNmFyl8dbsL_Qu^@cOU>mGO_e>?Q zBuA6NCqgnxj6tuHgt(aEOS&Xt4;;tsoDzbQ*RXgG<|j>FUAQfLB#UBdaz;p|1w5I? z0v0}=OGq${KzczxoS$bjmP|$kvW}>bPDmmzNSFnMWQ(Lf0a-@bCho<^(~-}@&%-%n zvqq>qRU8djSL3GZ*oNzv>T1zkEjeU$MyPD-+0EAT8?EQn)&Z?`Acrh&GK5gY^S!LM zWz&0d!+TQowrk$@9AfGs)a$qsys{Y_+X#-S!JAs}rrL8$>$#PGxy2qWd0YsYpUoyp zAPvAMF?ycPpo+i%-&ZL2Yy~cZ%=*j#;u)fgw^RVek^u{A*yNeW_ML(I?FArL5M_9$ zyA6ilDSXk?nwNG9J*FtMa@bH!fq%qg&fp&Dz-iqAHajbkjpjJ)g=WXuKP{w$`)Pb( z@rNabJfF9DDLS8!XT&+VXw!lIq!^DSr3-=LKw{<64}km~u4Sb7#-XmWPvjR5o<8_& z>G@K=r$zE=+H!W;M78ln_PIYaq*LtEo+E>QI|HwWiJ-vRg+1Z-iEYsLeS_Z8iFKm(@GG z*?WDX_qy6UruB|Nr|KKmfHsb6jpLt=WWCJ14{dOLhA=q&qd91QSn^d z^3*?aE53m0>CikKil<|X2wqiMx(PL^>IrL}u;K}C=W6$+W^AR&fQVfh7`$O%a2hR9 zPE?A-3`A(2{vGoYq+St8qnt$~3;`P3ml)oXVLm~)?OmZR!&HOT{is+8I7ub|*Gn+L zS4F&2DCIhiI+0|Vr|&ZpXdVoB0t_GeFCRy85oZXsAY8u>T~-5KwsLe$pwtQ3$XfF2 zgRxCZ78A?dKrkK^Wlpql_aikcC*XyPQIm(OksiYW#@CWkP4-$=VOQ*nMAWqfREq|D!9ajIA zyKc4R39~k>xC5#?pt%DON}QeRE!p=QbBJ~WNPw&|S?|#`ui|Zinq`|FkN#qG?MGh> z|9bdWSAKIvWlwADX@xz#owJe=3@9+P800&m?;nKh^;P1wA@Wp^+TQ-B1JxYOAj7`aYA#PP|7MyVmrscKxqHs z@-B}`7h%^P9vbA(FFOZW=8dBU*f*?Yu)+SOfgyY&MffIz=Z;=#VcrZlfqv~cbg7y7 zy4ebRW$<1AcvPR4u2OexRcEH(R-KvZRcEGGNiKP>2x)L0q0&%Yo;#LGuUTLl%L;K) zW=FBaWT*_Y2muX%Tsaie8H;%gg{sW0;}EFLD~SBxD*#7oo(-&kdB7RZ}jCu2lidqPIaQ3FKh zK)#oZc>!eKvCm?KqZ*J(X3jX<%gmg1gFKK*W=_?$zdo+?T-JIbeIXX%GjU3uYAq;$DvidJkKEpST7>@o~k)KNn7jQqk0^}pmyIin8WeNQBKNX!WzjjMQq9@yRD@a!L5dD-1 0: - url_list = get_download_list(DOWNLOAD_LIST_TXT) - - for url in url_list: - webtoon = None - if 'tw.kakaowebtoon.com' in url: - webtoon_id = url.split('/')[-1] - for cookie in valid_cookies: - if webtoon_id in get_kakao_urls(URL_TYPE): - webtoon = KakaoWebtoon(webtoon_id, cookie) - webtoon.download_webtoon(url, DOWNLOAD_DIR) - elif DOWNLOAD_WEBTOON and 'www.webtoons.com' in url: - webtoon_id = url.split('=')[1] - webtoon = Webtoon(webtoon_id) - webtoon.download_webtoon(url, DOWNLOAD_DIR) - elif 'www.bomtoon.tw' in url: - webtoon_id = url.split('/')[-1] - webtoon = Bomtoon(webtoon_id) - webtoon.download_webtoon(url, DOWNLOAD_DIR) - if webtoon is not None and webtoon.new_webtoon != "": - new_webtoons.append(webtoon.new_webtoon) + url_list = get_download_list(DOWNLOAD_LIST_TXT) + + for url in url_list: + webtoon = None + if 'webtoon.kakao.com' in url: + webtoon_id = url.split('/')[-1] + if webtoon_id in kakao_ids: + webtoon = KakaoWebtoon(webtoon_id, COOKIE) + webtoon.download_webtoon(url, DOWNLOAD_DIR) + elif DOWNLOAD_WEBTOON and 'www.webtoons.com' in url: + webtoon_id = url.split('=')[1] + webtoon = Webtoon(webtoon_id) + webtoon.download_webtoon(url, DOWNLOAD_DIR) + if webtoon is not None and webtoon.new_webtoon != "": + new_webtoons.append(webtoon.new_webtoon) print(new_webtoons) + # temp_url_list = get_download_list(TEMP_DOWNLOAD_LIST_TXT) + # for temp_url in temp_url_list: + # if 'webtoon.kakao.com' in temp_url: + # webtoon_id = temp_url.split('/')[-1] + # webtoon = KakaoWebtoon(webtoon_id, COOKIE) + # webtoon.download_webtoon(temp_url, TEMP_DOWNLOAD_DIR) + def convert(): for webtoon_path in DOWNLOAD_DIR.iterdir(): if len(new_webtoons) > 0: @@ -87,13 +72,21 @@ def main(): convert() if __name__ == "__main__": - set_valid_cookie() - - task = TASK_TYPE - if 'd' in task: download() if 'c' in task: + # new_webtoons.append('1995青春報告') + # new_webtoons.append('Unsleep') + # new_webtoons.append('鬼夜曲') + # new_webtoons.append('Backlight') + # new_webtoons.append('鄰居是公會成員') + # new_webtoons.append('No Moral') + # new_webtoons.append('披薩外送員與黃金宮') + # new_webtoons.append('PAYBACK') + # new_webtoons.append('融冰曲線') + # new_webtoons.append('夢龍傳') + # new_webtoons.append('棋子的世界') + # new_webtoons.append('監禁倉庫') + # new_webtoons.append('易地思之') convert() - print('MyWebtoon') - + print('MyWebtoon') \ No newline at end of file diff --git a/rename.py b/rename.py index ab5a9d6..889be8b 100644 --- a/rename.py +++ b/rename.py @@ -1,17 +1,29 @@ import os +from pathlib import Path from data.path_constant import DOWNLOAD_DIR, NETWORK_DIR -from prerequisite import rename_episodes +from helper.prerequisite import rename_episodes -rename_episodes(DOWNLOAD_DIR) -rename_episodes(NETWORK_DIR) +# rename_episodes(DOWNLOAD_DIR) +# rename_episodes(NETWORK_DIR) -for first_level_path in NETWORK_DIR.iterdir(): - if first_level_path.name == '怪力亂神': - for second_level_path in first_level_path.iterdir(): - if "話." in second_level_path.name: - episode_name = second_level_path.name.replace("話.", "話 ") +# for first_level_path in NETWORK_DIR.iterdir(): +# if first_level_path.name == '怪力亂神': +# for second_level_path in first_level_path.iterdir(): +# if "話." in second_level_path.name: +# episode_name = second_level_path.name.replace("話.", "話 ") - new_path = first_level_path / episode_name - print(second_level_path) - print(new_path) - os.rename(second_level_path, new_path) \ No newline at end of file +# new_path = first_level_path / episode_name +# print(second_level_path) +# print(new_path) +# os.rename(second_level_path, new_path) + +download_dir = Path('C:/') / 'Users' / 'ithil' / 'Downloads' +download_dir = Path('E:/') / 'Webtoon' / 'PAYBACK' / '0.序章' + +for image in download_dir.iterdir(): + name = image.name + # new_name = str(int(name.split('.')[0]) - 10) + '.webp' + new_name = f"{(int(image.stem) -10):03d}{image.suffix}" + new_path = image.with_name(new_name) # 生成新路径 + image.rename(new_path) # 重命名文件 + print(f"重命名: {image.name} → {new_name}") \ No newline at end of file