Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/CommonFormats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const Category = {
TEXT: "text",
AUDIO: "audio",
ARCHIVE: "archive",
IMAGE_ARCHIVE: "image archive",
SPREADSHEET: "spreadsheet",
PRESENTATION: "presentation",
FONT: "font",
Expand Down Expand Up @@ -199,6 +200,20 @@ const CommonFormats = {
"application/x-tar",
Category.ARCHIVE
),
RAR: new FormatDefinition(
"Rar Archive",
"rar",
"rar",
"application/vnd.rar",
Category.ARCHIVE
),
SZ: new FormatDefinition(
"7z Archive",
"7z",
"7z",
"application/x-7z-compressed",
Category.ARCHIVE
),
// documents
PDF: new FormatDefinition(
"Portable Document Format",
Expand Down
6 changes: 6 additions & 0 deletions src/TraversionGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,17 @@ export class TraversionGraph {
{from: "image", to: "text", cost: 0.5}, // Depends on the content and method, but can be relatively efficient for simple images
{from: "text", to: "audio", cost: 0.6}, // Somewhat lossy for anything that isn't speakable text
{from: "document", to: "text", cost: 1}, // Often very lossy, loses rich formatting
{from: "image", to: "image archive", cost: -1}, // Prioritize immensely
];
private categoryAdaptiveCosts: CategoryAdaptiveCost[] = [
{ categories: ["text", "image", "audio"], cost: 15 }, // Text to audio through an image is likely not what the user wants
{ categories: ["image", "video", "audio"], cost: 10000 }, // Converting from image to audio through video is especially lossy
{ categories: ["audio", "video", "image"], cost: 10000 }, // Converting from audio to image through video is especially lossy

{ categories: ["archive", "image", "archive"], cost: 10000 }, // If archive -> archive is possible directly, it should be prioritized pretty much always.
{ categories: ["image archive", "image", "image archive"], cost: 10000 }, // As above
{ categories: ["archive", "image", "image archive"], cost: 10000 }, // As above
{ categories: ["image archive", "image", "archive"], cost: 10000 }, // As above
];
// Keeps track of path segments that have failed when attempted during the last run
private temporaryDeadEnds: ConvertPathNode[][] = [];
Expand Down
141 changes: 60 additions & 81 deletions src/handlers/comics.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// file: comics.ts

import type { FileData, FileFormat, FormatHandler } from "../FormatHandler.ts";
import CommonFormats from "src/CommonFormats.ts";
import CommonFormats, { Category } from "src/CommonFormats.ts";

import {
createTar,
Expand All @@ -14,56 +14,32 @@ import {
import JSZip from "jszip";

const image_list = ["png","jpg","webp","bmp","tiff","gif"];
const archives_list = ["zip","cbz","tar","cbt"];

class comicsHandler implements FormatHandler {

public name: string = "comics";
export class comicsZipHandler implements FormatHandler {
public name: string = "comicsZip";
public supportedFormats?: FileFormat[];
public ready: boolean = false;

async init () {
this.supportedFormats = [
CommonFormats.PNG.supported("png", true, true),
CommonFormats.JPEG.supported("jpg", true, true),
CommonFormats.WEBP.supported("webp", true, true),
CommonFormats.BMP.supported("bmp", true, true),
CommonFormats.TIFF.supported("tiff", true, true),
CommonFormats.GIF.supported("gif", true, true),

CommonFormats.ZIP.supported("zip", true, true),
{
name: "Tape Archive",
format: "tar",
extension: "tar",
mime: "application/x-tar",
from: true,
to: true,
internal: "tar",
category: ["archive"],
},
CommonFormats.PNG.supported("png", false, true),
CommonFormats.JPEG.supported("jpg", false, true),
CommonFormats.WEBP.supported("webp", false, true),
CommonFormats.BMP.supported("bmp", false, true),
CommonFormats.TIFF.supported("tiff", false, true),
CommonFormats.GIF.supported("gif", false, true),

CommonFormats.ZIP.supported("zip", true, false),
{
name: "Comic Book Archive (ZIP)",
format: "cbz",
extension: "cbz",
mime: "application/vnd.comicbook+zip",
from: true,
to: true,
to: false,
internal: "cbz",
category: ["archive"],
lossless: true,
},
{
name: "Comic Book Archive (TAR)",
format: "cbt",
extension: "cbt",
mime: "application/vnd.comicbook+tar",
from: true,
to: true,
internal: "cbt",
category: ["archive"],
lossless: true,
category: [Category.ARCHIVE,Category.IMAGE_ARCHIVE],
lossless: false,
},
];

Expand All @@ -77,35 +53,8 @@ class comicsHandler implements FormatHandler {
): Promise<FileData[]> {
const outputFiles: FileData[] = [];

// Base name for imgs -> archive
const baseName = inputFiles[0].name.replace("_0."+inputFormat.extension,"."+inputFormat.extension).split(".").slice(0, -1).join(".");

// Single-gif catching
if (inputFormat.internal === "gif" && (archives_list.includes(outputFormat.internal)) && inputFiles.length === 1) {
throw new Error("User probably intends for an archive of video/gif frames; abort.");
}

// Pack a zip/cbz with code copied from wad.ts
if ((image_list.includes(inputFormat.internal)) && (outputFormat.internal === "cbz" || outputFormat.internal === "zip")) {
const zip = new JSZip();

// Add files to archive
let iterations = 0;
for (const file of inputFiles) {
if (outputFormat.internal === "cbz") {
zip.file("Page "+String(iterations)+"."+inputFormat.extension, file.bytes);
}
else {
zip.file(file.name, file.bytes);
}
iterations += 1;
}

const output = await zip.generateAsync({ type: "uint8array" });
outputFiles.push({ bytes: output, name: baseName + "." + outputFormat.extension });
}
// Unpack a zip/cbz with code copied from lzh.ts
else if ((inputFormat.internal === "cbz" || inputFormat.internal === "zip") && (image_list.includes(outputFormat.internal))) {
if ((inputFormat.internal === "cbz" || inputFormat.internal === "zip") && (image_list.includes(outputFormat.internal))) {
for (const file of inputFiles) {
const zip = new JSZip();
await zip.loadAsync(file.bytes);
Expand Down Expand Up @@ -135,16 +84,54 @@ class comicsHandler implements FormatHandler {
throw new Error("No applicable files to unzip found.");
}
}
// Pack a cbt with code from tar.ts
else if (image_list.includes(inputFormat.internal) && outputFormat.internal === "cbt") {
const bytes = createTar(
inputFiles.map(file => ({ name: "Page "+inputFiles.indexOf(file)+"."+inputFormat.extension, data: file.bytes })),
{},
);
outputFiles.push({ bytes: bytes, name: baseName + "." + outputFormat.extension });
else {
throw new Error("Invalid input-output.");
}

return outputFiles;
}
}

export class comicsTarHandler implements FormatHandler {
public name: string = "comicsTar";
public supportedFormats?: FileFormat[];
public ready: boolean = false;

async init () {
this.supportedFormats = [
CommonFormats.PNG.supported("png", false, true),
CommonFormats.JPEG.supported("jpg", false, true),
CommonFormats.WEBP.supported("webp", false, true),
CommonFormats.BMP.supported("bmp", false, true),
CommonFormats.TIFF.supported("tiff", false, true),
CommonFormats.GIF.supported("gif", false, true),

CommonFormats.TAR.supported("tar", true, false),
{
name: "Comic Book Archive (TAR)",
format: "cbt",
extension: "cbt",
mime: "application/vnd.comicbook+tar",
from: true,
to: false,
internal: "cbt",
category: [Category.ARCHIVE,Category.IMAGE_ARCHIVE],
lossless: false,
},
];

this.ready = true;
}

async doConvert (
inputFiles: FileData[],
inputFormat: FileFormat,
outputFormat: FileFormat
): Promise<FileData[]> {
const outputFiles: FileData[] = [];

// Unpack a tar/cbt with code from tar.ts
else if ((inputFormat.internal === "cbt" || inputFormat.internal === "tar") && image_list.includes(outputFormat.internal)) {
if ((inputFormat.internal === "cbt" || inputFormat.internal === "tar") && image_list.includes(outputFormat.internal)) {
for (const inputFile of inputFiles) {
const files = parseTar(inputFile.bytes);

Expand Down Expand Up @@ -172,18 +159,10 @@ class comicsHandler implements FormatHandler {
throw new Error("No applicable files to unpack found.");
}
}
// Renaming interchangeable formats. Note that any valid "comic book" archive can be guaranteed as a valid standard archive, but not every valid archive can be a valid comic book archive. Thus, we only allow renaming from comic book to non-comic book formats.
else if ((inputFormat.internal === "cbz" && outputFormat.internal === "zip") || (inputFormat.internal === "cbt" && outputFormat.internal === "tar")) {
for (const file of inputFiles) {
outputFiles.push({ bytes: file.bytes, name: file.name.split(".").slice(0, -1).join(".") + "." + outputFormat.extension });
}
}
else {
throw new Error("Invalid input-output.");
}

return outputFiles;
}
}

export default comicsHandler;
15 changes: 10 additions & 5 deletions src/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import curaniHandler from "./curani.ts";
import bunburrowsHandler from "./bunburrows.ts";
import rgbaHandler from "./rgba.ts";
import svgTraceHandler from "./svgTrace.ts";
import { renameZipHandler, renameTxtHandler, renameJsonHandler } from "./rename.ts";
import { comicsZipHandler, comicsTarHandler } from "./comics.ts";
import { renameZipHandler, renameRarHandler, renameTarHandler, rename7zHandler, renameTxtHandler, renameJsonHandler } from "./rename.ts";
import envelopeHandler from "./envelope.ts";
import pandocHandler from "./pandoc.ts";
import svgForeignObjectHandler from "./svgForeignObject.ts";
Expand All @@ -37,7 +38,7 @@ import jsonToCHandler from "./jsonToC.ts";
import turbowarpHandler from "./turbowarp.ts";
import libopenmptHandler from "./libopenmpt.ts";
import { midiCodecHandler, midiSynthHandler } from "./midi.ts";
import lzhHandler from "./lzh.ts";
import { LZHHandler, LZH2Handler } from "./lzh.ts";
import txtToInfiniteCraftHandler from "./txtToInfiniteCraft.ts";
import wadHandler from "./wad.ts";
import espeakngHandler from "./espeakng.js"
Expand All @@ -54,7 +55,6 @@ import vexflowHandler from "./vexflow.ts";
import toonHandler from "./toon.ts";
import rpgmvpHandler from "./rpgmvp.ts";
import otaHandler from "./ota.ts";
import comicsHandler from "./comics.ts";
import terrariaWldHandler from "./terrariawld.ts";
import opusMagnumHandler from "./opusMagnum.ts";
import aperturePictureHandler from "./aperturePicture.ts";
Expand All @@ -80,7 +80,12 @@ try { handlers.push(new ImageMagickHandler()) } catch (_) { };
try { handlers.push(new curaniHandler()) } catch (_) { };
try { handlers.push(new bunburrowsHandler()) } catch (_) { };
try { handlers.push(new rgbaHandler()) } catch (_) { };
try { handlers.push(new comicsZipHandler()) } catch (_) { };
try { handlers.push(new comicsTarHandler()) } catch (_) { };
try { handlers.push(renameZipHandler) } catch (_) { };
try { handlers.push(renameTarHandler) } catch (_) { };
try { handlers.push(renameRarHandler) } catch (_) { };
try { handlers.push(rename7zHandler) } catch (_) { };
try { handlers.push(renameTxtHandler) } catch (_) { };
try { handlers.push(renameJsonHandler) } catch (_) { };
try { handlers.push(new envelopeHandler()) } catch (_) { };
Expand Down Expand Up @@ -110,7 +115,8 @@ try { handlers.push(new jsonToCHandler()) } catch (_) { };
try { handlers.push(new libopenmptHandler()) } catch (_) { };
try { handlers.push(new midiCodecHandler()) } catch (_) { };
try { handlers.push(new midiSynthHandler()) } catch (_) { };
try { handlers.push(new lzhHandler()) } catch (_) { };
try { handlers.push(new LZHHandler()) } catch (_) { };
try { handlers.push(new LZH2Handler()) } catch (_) { };
try { handlers.push(new wadHandler()) } catch (_) { };
try { handlers.push(new pandocHandler()) } catch (_) { };
try { handlers.push(new txtToInfiniteCraftHandler()) } catch (_) { };
Expand All @@ -128,7 +134,6 @@ try { handlers.push(new vexflowHandler()) } catch (_) { };
try { handlers.push(new toonHandler()) } catch (_) { };
try { handlers.push(new rpgmvpHandler()) } catch (_) { };
try { handlers.push(new otaHandler()) } catch (_) { };
try { handlers.push(new comicsHandler()) } catch (_) { };
try { handlers.push(new terrariaWldHandler()) } catch (_) { };
try { handlers.push(new opusMagnumHandler()) } catch (_) { };
try { handlers.push(new aperturePictureHandler()) } catch (_) { };
Expand Down
Loading
Loading