Skip to content

Commit

Permalink
Merge branch '4-download-command' into 'main'
Browse files Browse the repository at this point in the history
Resolve "Download Command"

Closes #4

See merge request MrMysterius/mangasee-dl!9
  • Loading branch information
MrMysterius committed Jan 30, 2023
2 parents 57a7420 + 9cf70c1 commit af6b8c7
Show file tree
Hide file tree
Showing 8 changed files with 283 additions and 21 deletions.
16 changes: 9 additions & 7 deletions archive.ts
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;
}
2 changes: 1 addition & 1 deletion build_linux.sh
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
2 changes: 1 addition & 1 deletion build_windows.sh
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
12 changes: 6 additions & 6 deletions comic-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { BasicMangaMetadata, Chapter } from "./metadata.ts";

export interface PageInfo {
index: number;
width: number;
height: number;
size: number;
filename: string;
}

Expand All @@ -30,10 +29,11 @@ export function writeComicInfo(folder_path: string, metadata: BasicMangaMetadata
}
);
Deno.writeFileSync(FILE_PATH, Encoder.encode(`\t<Series>${metadata.title}</Series>\n`), { create: false, append: true });
Deno.writeFileSync(FILE_PATH, Encoder.encode(`\t<Number>${chapter_info.main}</Number>\n`), { create: false, append: true });
Deno.writeFileSync(FILE_PATH, Encoder.encode(`\t<Number>${chapter_info.main}${chapter_info.sub != 0 ? `.${chapter_info.sub}` : ""}</Number>\n`), {
create: false,
append: true,
});
Deno.writeFileSync(FILE_PATH, Encoder.encode(`\t<Volume>${chapter_info.pretype}</Volume>\n`), { create: false, append: true });
if (chapter_info.sub != 0)
Deno.writeFileSync(FILE_PATH, Encoder.encode(`\t<AlternateNumber>${chapter_info.sub}</AlternateNumber>\n`), { create: false, append: true });
Deno.writeFileSync(FILE_PATH, Encoder.encode(`\t<Notes>Downloaded/Scrapped with Mangasee123 Downloader from MrMysterius</Notes>\n`), {
create: false,
append: true,
Expand All @@ -47,7 +47,7 @@ export function writeComicInfo(folder_path: string, metadata: BasicMangaMetadata
Deno.writeFileSync(FILE_PATH, Encoder.encode(`\t<Pages>\n`), { create: false, append: true });

for (const page of pages) {
Deno.writeFileSync(FILE_PATH, Encoder.encode(`\t\t<Page Image="${page.index}" ImageWidth="${page.width}" ImageHeight="${page.height}">\n`), {
Deno.writeFileSync(FILE_PATH, Encoder.encode(`\t\t<Page Image="${page.index}" ImageSize="${page.size}" />\n`), {
create: false,
append: true,
});
Expand Down
154 changes: 154 additions & 0 deletions download.ts
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));
}
104 changes: 103 additions & 1 deletion main.ts
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 || "";
Expand All @@ -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;
}
4 changes: 4 additions & 0 deletions metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,14 @@ export function extractMetadata(html_document: string) {
switch (label?.innerText) {
case "Author(s):": {
child.querySelectorAll("a").forEach((a) => {
//@ts-ignore property innerText does exist but is not in the Interface/Declaration
metadata.authors.push(a.innerText);
});
continue;
}
case "Genre(s):": {
child.querySelectorAll("a").forEach((a) => {
//@ts-ignore property innerText does exist but is not in the Interface/Declaration
metadata.genres.push(a.innerText);
});
continue;
Expand Down Expand Up @@ -87,6 +89,7 @@ export interface Chapter {
pretype: number;
main: number;
sub: number;
raw: string;
}

export async function getChapters(mangaIndexName: string) {
Expand All @@ -107,6 +110,7 @@ export function extractChapters(html_document: string) {
pretype: parseInt(match[1]),
main: parseInt(match[2]),
sub: parseInt(match[3]),
raw: chapter.Chapter,
});
}

Expand Down
Loading

0 comments on commit af6b8c7

Please sign in to comment.