-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch '4-download-command' into 'main'
Resolve "Download Command" Closes #4 See merge request MrMysterius/mangasee-dl!9
- Loading branch information
Showing
8 changed files
with
283 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,18 @@ | ||
export async function archive(src_folder: string, archive_name: string): Promise<boolean> { | ||
const cmd: string[] = ["7z", "a", archive_name]; | ||
import { normalize, posix } from "https://deno.land/[email protected]/path/mod.ts"; | ||
|
||
for (const file of Deno.readDirSync(src_folder)) { | ||
if (file.isSymlink) continue; | ||
cmd.push(`${src_folder}/${file.name}`); | ||
} | ||
export async function archive(src_folder: string, archive_name: string, delete_src = true): Promise<boolean> { | ||
try { | ||
Deno.removeSync(archive_name, { recursive: true }); | ||
} catch (_e) {} | ||
const cmd: string[] = ["7z", "a", archive_name, posix.normalize(`${posix.normalize(normalize(`${src_folder}`))}\\*`)]; | ||
|
||
const process = Deno.run({ | ||
cmd, | ||
stdout: "piped", | ||
stderr: "piped", | ||
}); | ||
|
||
return (await process.status()).success; | ||
if (!(await process.status()).success) return false; | ||
if (delete_src) Deno.removeSync(src_folder, { recursive: true }); | ||
return true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
# Linux | ||
deno compile --allow-net --allow-read --allow-write --allow-env --target x86_64-unknown-linux-gnu --output mangasee-dl main.ts | ||
deno compile --allow-net --allow-read --allow-write --allow-env --allow-run --target x86_64-unknown-linux-gnu --output mangasee-dl main.ts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
# Windows | ||
deno compile --allow-net --allow-read --allow-write --allow-env --target x86_64-pc-windows-msvc --output mangasee-dl.exe main.ts | ||
deno compile --allow-net --allow-read --allow-write --allow-env --allow-run --target x86_64-pc-windows-msvc --output mangasee-dl.exe main.ts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import { BasicMangaMetadata, Chapter } from "./metadata.ts"; | ||
import { PageInfo } from "./comic-info.ts"; | ||
import { join } from "https://deno.land/[email protected]/path/mod.ts"; | ||
import { saveProgress } from "./progress.ts"; | ||
import { writeComicInfo } from "./comic-info.ts"; | ||
import { archive } from "./archive.ts"; | ||
import * as Color from "https://deno.land/[email protected]/fmt/colors.ts"; | ||
import { zeroSpaceOut } from "./main.ts"; | ||
|
||
const BASE_CHAPTER_URL = "https://mangasee123.com/read-online/"; | ||
|
||
export async function getChapterPage(mangaIndexName: string, chapter: Chapter) { | ||
if (chapter.pretype == 1) { | ||
const res = await fetch(`${BASE_CHAPTER_URL}${mangaIndexName}-chapter-${chapter.main}${chapter.sub != 0 ? `.${chapter.sub}` : ""}.html`); | ||
return await res.text(); | ||
} else { | ||
const res = await fetch( | ||
`${BASE_CHAPTER_URL}${mangaIndexName}-chapter-${chapter.main}${chapter.sub != 0 ? `.${chapter.sub}` : ""}-index-${chapter.pretype}.html` | ||
); | ||
return await res.text(); | ||
} | ||
} | ||
|
||
interface ChapterInfo { | ||
Chapter: string; | ||
Page: string; | ||
Directory: string; | ||
} | ||
|
||
export function extractChapterInfo(html_document: string): ChapterInfo | null { | ||
const match = html_document.match(/vm\.CurChapter = (.+);/m); | ||
return JSON.parse(match?.[1] || "[]") || null; | ||
} | ||
|
||
export function extractHost(html_document: string) { | ||
const match = html_document.match(/vm\.CurPathName = "(.+)";/m); | ||
return match?.[1] || null; | ||
} | ||
|
||
export async function downloadChapter(mangaIndexName: string, chapter: Chapter, folder_path: string): Promise<PageInfo[] | null> { | ||
const html_document = await getChapterPage(mangaIndexName, chapter); | ||
const chapter_info = extractChapterInfo(html_document); | ||
const host = extractHost(html_document); | ||
|
||
if (chapter_info == null || host == null) return null; | ||
|
||
let chapter_number = ""; | ||
const main_number_string = chapter.main.toString(); | ||
if (main_number_string.length <= 4) { | ||
chapter_number = "0".repeat(4 - main_number_string.length) + main_number_string; | ||
} else { | ||
chapter_number = main_number_string; | ||
} | ||
if (chapter.sub != 0) { | ||
chapter_number += `.${chapter.sub}`; | ||
} | ||
|
||
const DIRECTORY_STRING = chapter_info.Directory != "" ? `${chapter_info.Directory}/` : ""; | ||
const CHAPTER_PREFIX = `${chapter_number}-`; | ||
const BASE_DOWNLOAD_URL = `https://${host}/manga/${mangaIndexName}/${DIRECTORY_STRING}${CHAPTER_PREFIX}`; | ||
|
||
const pages: PageInfo[] = []; | ||
|
||
const page_count = parseInt(chapter_info.Page); | ||
for (let i = 1; i <= page_count; i++) { | ||
let page_number = ""; | ||
const i_string = i.toString(); | ||
if (i_string.length <= 3) { | ||
page_number = "0".repeat(3 - i_string.length) + i_string; | ||
} else { | ||
page_number = i_string; | ||
} | ||
|
||
const res = await fetch(`${BASE_DOWNLOAD_URL}${page_number}.png`); | ||
const file = await Deno.open(join(folder_path, `${i}.png`), { create: true, write: true }); | ||
|
||
await res.body?.pipeTo(file.writable); | ||
try { | ||
file.close(); | ||
} catch (_e) {} | ||
const size = parseInt(res.headers.get("content-length") as string); | ||
|
||
pages.push({ index: i - 1, filename: `${i}.png`, size: size }); | ||
} | ||
|
||
return pages; | ||
} | ||
|
||
export async function download( | ||
mangaIndexName: string, | ||
metadata: BasicMangaMetadata, | ||
folder_path: string, | ||
chapters: Chapter[], | ||
start: Chapter, | ||
end: Chapter, | ||
current: Chapter | null = null | ||
) { | ||
const BASE_FOLDER_PATH = join(folder_path, mangaIndexName); | ||
Deno.mkdirSync(BASE_FOLDER_PATH, { recursive: true }); | ||
|
||
if (current == null) current = start; | ||
if (!findChapter(chapters, current)?.chapter) return false; | ||
|
||
chapters = chaptersSort(chapters); | ||
let init = true; | ||
|
||
for (let chapter of chapters) { | ||
if (chapter.raw != current.raw && init) { | ||
continue; | ||
} | ||
init = false; | ||
|
||
Deno.stdout.write( | ||
new TextEncoder().encode(`${Color.green("Downloading: ")} ${Color.blue(`v${chapter.pretype} - c${zeroSpaceOut(chapter.main, 4)}.${chapter.sub}`)}`) | ||
); | ||
|
||
current = chapter; | ||
saveProgress(BASE_FOLDER_PATH, { current: current, start: start, end: end }); | ||
|
||
const CHAPTER_PATH = join(BASE_FOLDER_PATH, `${mangaIndexName}-v${current.pretype}-c${current.main}${current.sub != 0 ? `.${current.sub}` : ""}`); | ||
try { | ||
Deno.removeSync(CHAPTER_PATH, { recursive: true }); | ||
} catch (_e) {} | ||
Deno.mkdirSync(CHAPTER_PATH, { recursive: true }); | ||
const pages = await downloadChapter(mangaIndexName, current, CHAPTER_PATH); | ||
if (pages == null) return false; | ||
|
||
Deno.stdout.write(new TextEncoder().encode(`${Color.white(" Done |")} ${Color.yellow("Writing Metadata")}`)); | ||
if (!writeComicInfo(CHAPTER_PATH, metadata, current, pages)) return false; | ||
Deno.stdout.write(new TextEncoder().encode(`${Color.white(" Done |")} ${Color.magenta("Archiving")}`)); | ||
if (!(await archive(CHAPTER_PATH, `${CHAPTER_PATH}.cb7`))) return false; | ||
Deno.stdout.write(new TextEncoder().encode(`${Color.white(" Done |\n")}`)); | ||
|
||
if (chapter.raw == end.raw) break; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
export interface PartChapter { | ||
pretype: number; | ||
main: number; | ||
sub: number; | ||
} | ||
|
||
export function findChapter(chapters: Chapter[], search: PartChapter) { | ||
const index = chapters.findIndex((chapter) => chapter.pretype == search.pretype && chapter.main == search.main && chapter.sub == search.sub); | ||
if (index == -1) return null; | ||
return { chapter: chapters[index], index }; | ||
} | ||
|
||
export function chaptersSort(chapters: Chapter[]) { | ||
return chapters.sort((a, b) => parseInt(a.raw) - parseInt(b.raw)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,25 @@ | ||
import { parse } from "https://deno.land/[email protected]/flags/mod.ts"; | ||
import * as Color from "https://deno.land/[email protected]/fmt/colors.ts"; | ||
import { searchAnime } from "./search.ts"; | ||
import { getBasicMetadata } from "./metadata.ts"; | ||
import { Chapter, getBasicMetadata, getFullMetadata } from "./metadata.ts"; | ||
import { download, chaptersSort, findChapter } from "./download.ts"; | ||
import { loadProgress } from "./progress.ts"; | ||
import { join } from "https://deno.land/[email protected]/path/mod.ts"; | ||
|
||
const ARGS = parse(Deno.args); | ||
|
||
switch (ARGS._[0] || undefined) { | ||
case "search": { | ||
if (ARGS._.length <= 1) break; | ||
|
||
const results = await searchAnime(ARGS._.slice(1, ARGS._.length).join(" "), parseInt(ARGS.l) || parseInt(ARGS["limit"]) || 5); | ||
|
||
for (const result of results) { | ||
if (result.score > (parseInt(ARGS.s) || parseInt(ARGS["score-threshold"]) || 0.05)) continue; | ||
|
||
let release_year = ""; | ||
let chapters_available = ""; | ||
|
||
if (!ARGS["no-metadata"] && !ARGS.n) { | ||
const metadata = await getBasicMetadata(result.item.i); | ||
release_year = metadata?.release_year || ""; | ||
|
@@ -26,7 +33,102 @@ switch (ARGS._[0] || undefined) { | |
} | ||
break; | ||
} | ||
case "download": { | ||
if (!ARGS._[1]) break; | ||
|
||
const metadata = await getFullMetadata(ARGS._[1] as string); | ||
if (metadata == null) break; | ||
metadata.chapters = chaptersSort(metadata.chapters); | ||
|
||
const progress = loadProgress(join(Deno.cwd(), ARGS._[1] as string)); | ||
const start_part = ARGS.s?.toString().split(".") || ARGS["start"]?.toString().split(".") || null; | ||
const end_part = ARGS.e?.toString().split(".") || ARGS["end"]?.toString().split(".") || null; | ||
|
||
let start: Chapter = { pretype: 0, main: 0, sub: 0, raw: "" }; | ||
let end: Chapter = { pretype: 0, main: 0, sub: 0, raw: "" }; | ||
let current: Chapter = { pretype: 0, main: 0, sub: 0, raw: "" }; | ||
|
||
if (progress != undefined) { | ||
start = progress.start; | ||
end = progress.end; | ||
current = progress.current; | ||
} | ||
|
||
if (start_part != null) { | ||
let c: Chapter = start; | ||
switch (start_part.length) { | ||
case 3: | ||
c = { pretype: start_part[0], main: start_part[1], sub: start_part[2], raw: `${start_part[0]}${zeroSpaceOut(start_part[1], 4)}${start_part[2]}` }; | ||
break; | ||
case 2: | ||
c = { pretype: 1, main: start_part[0], sub: start_part[1], raw: `1${zeroSpaceOut(start_part[0], 4)}${start_part[1]}` }; | ||
break; | ||
case 1: | ||
c = { pretype: 1, main: start_part[0], sub: 0, raw: `1${zeroSpaceOut(start_part[0], 4)}0` }; | ||
break; | ||
} | ||
if (start.raw != c.raw) { | ||
start = c; | ||
current = start; | ||
} | ||
} else if (progress == undefined) { | ||
start = metadata.chapters[0]; | ||
current = metadata.chapters[0]; | ||
} | ||
|
||
if (end_part != null) { | ||
let c = end; | ||
switch (end_part.length) { | ||
case 3: | ||
c = { pretype: end_part[0], main: end_part[1], sub: end_part[2], raw: `${end_part[0]}${zeroSpaceOut(end_part[1], 4)}${end_part[2]}` }; | ||
break; | ||
case 2: | ||
c = { pretype: 1, main: end_part[0], sub: end_part[1], raw: `1${zeroSpaceOut(end_part[0], 4)}${end_part[1]}` }; | ||
break; | ||
case 1: | ||
c = { pretype: 1, main: end_part[0], sub: 0, raw: `1${zeroSpaceOut(end_part[0], 4)}0` }; | ||
break; | ||
} | ||
if (end.raw != c.raw) { | ||
const find_end = findChapter(metadata.chapters, end); | ||
const find_c = findChapter(metadata.chapters, c); | ||
if (find_end == null) { | ||
end = c; | ||
} else if (find_c != null && find_end.index <= find_c.index) { | ||
end = c; | ||
} else if (find_c != null && find_end.index > find_c.index) { | ||
end = c; | ||
current = start; | ||
} | ||
} | ||
} else if (progress == undefined) { | ||
end = metadata.chapters[metadata.chapters.length - 1]; | ||
} | ||
|
||
console.log(Color.green("# Starting Download #")); | ||
console.log(Color.magenta(`Manga: ${metadata.basic.title}`)); | ||
console.log(Color.yellow(`Author(s): ${metadata.basic.authors.join(", ")}`)); | ||
console.log(Color.cyan(`Genre(s): ${metadata.basic.genres.join(", ")}`)); | ||
|
||
console.log(Color.bold("\nFrom:\t\t"), Color.blue(`v${start.pretype} - c${zeroSpaceOut(start.main, 4)}.${start.sub}`)); | ||
console.log(Color.bold("To:\t\t"), Color.brightYellow(`v${end.pretype} - c${zeroSpaceOut(end.main, 4)}.${end.sub}`)); | ||
console.log(Color.bold("Continuing:\t"), Color.brightRed(`v${current.pretype} - c${zeroSpaceOut(current.main, 4)}.${current.sub}`)); | ||
|
||
console.log(Color.gray("\n----------------------------------------")); | ||
|
||
await download(ARGS._[1] as string, metadata.basic, Deno.cwd(), metadata.chapters, start, end, current); | ||
|
||
console.log(Color.green("# Finished Download #")); | ||
|
||
break; | ||
} | ||
default: | ||
console.log(Color.red("# No Command Supplied - Aborting #")); | ||
break; | ||
} | ||
|
||
export function zeroSpaceOut(number: string | number, spaces: number) { | ||
const number_string = number.toString(); | ||
if (number_string.length >= spaces) return number_string; | ||
return "0".repeat(spaces - number_string.length) + number_string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.