initial sveltekit port
12
.editorconfig
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = false
|
||||
insert_final_newline = false
|
||||
8
.gitignore
vendored
|
|
@ -7,4 +7,10 @@ pnpm-lock.yaml
|
|||
/.routify/
|
||||
|
||||
.env
|
||||
dist
|
||||
dist
|
||||
.svelte-kit
|
||||
/package
|
||||
/build
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
1
.nvmrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
17
|
||||
4
.prettierrc
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"tabWidth": 4,
|
||||
"useTabs": false
|
||||
}
|
||||
4
_headers
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
/*
|
||||
Content-Security-Policy: default-src 'self'
|
||||
X-Content-Type-Options: nosniff
|
||||
cache-control: public, max-age=3600, must-revalidate
|
||||
12138
package-lock.json
generated
60
package.json
|
|
@ -1,67 +1,37 @@
|
|||
{
|
||||
"name": "svelte-app",
|
||||
"version": "1.0.0",
|
||||
"@comments scripts": {
|
||||
"dev": "develop with blazing fast rebuilds",
|
||||
"dev:features": "develop with features like SSR and serviceworker enabled",
|
||||
"build": "run build scripts below",
|
||||
"build:app": "build single page application (SPA)",
|
||||
"build:static": "Generate static pages",
|
||||
"serve": "serve content in 'dist' folder",
|
||||
"rollup": "run the rollup bundler",
|
||||
"nollup": "run the nollup no-bundler",
|
||||
"routify": "run routify"
|
||||
},
|
||||
"name": "mangades",
|
||||
"version": "1.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "run-p routify nollup",
|
||||
"dev:ssr": "run-p routify rollup",
|
||||
"build": "run-s build:*",
|
||||
"build:app": "routify -b && rollup -c",
|
||||
"build:static": "spank",
|
||||
"serve": "spassr --ssr",
|
||||
"rollup": "rollup -cw",
|
||||
"nollup": "nollup -c --verbose --host 0.0.0.0",
|
||||
"routify": "routify"
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"package": "svelte-kit package",
|
||||
"preview": "svelte-kit preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^19.0.2",
|
||||
"@rollup/plugin-node-resolve": "13.0.0",
|
||||
"@roxi/routify": "^2.18.8",
|
||||
"@sveltejs/adapter-auto": "1.0.0-next.90",
|
||||
"@sveltejs/kit": "1.0.0-next.572",
|
||||
"@types/streamsaver": "^2.0.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"fs-extra": "^10.1.0",
|
||||
"nollup": "^0.16.5",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.4.16",
|
||||
"postcss-import": "^14.1.0",
|
||||
"rollup": "^2.79.0",
|
||||
"rollup-plugin-hot": "^0.1.1",
|
||||
"rollup-plugin-livereload": "^2.0.5",
|
||||
"rollup-plugin-svelte": "^7.1.0",
|
||||
"rollup-plugin-svelte-hot": "^1.0.0-8",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-workbox": "^5.2.1",
|
||||
"spank": "^1.9.0",
|
||||
"spassr": "^2.6.0",
|
||||
"svelte": "^3.50.0",
|
||||
"svelte-check": "^2.10.1",
|
||||
"svelte-preprocess": "^4.10.7",
|
||||
"tossr": "^1.4.2"
|
||||
},
|
||||
"routify": {
|
||||
"extensions": "svelte,html,svx,md"
|
||||
},
|
||||
"spassr": {},
|
||||
"spank": {
|
||||
"blacklist": [
|
||||
"/example/modal/basic/4"
|
||||
]
|
||||
"tossr": "^1.4.2",
|
||||
"typescript": "^4.9.3",
|
||||
"vite-plugin-windicss": "^1.8.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "1.0.0-beta.3",
|
||||
"fflate": "^0.6.10",
|
||||
"streamsaver": "^2.0.6",
|
||||
"svelte-local-storage-store": "^0.3.1",
|
||||
"svelte-markdown": "^0.2.3",
|
||||
"swiper": "^8.3.2"
|
||||
"swiper": "^8.3.2",
|
||||
"vite": "^3.0.9"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
import svelte from 'rollup-plugin-svelte-hot';
|
||||
import Hmr from 'rollup-plugin-hot';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import livereload from 'rollup-plugin-livereload';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import { copySync, removeSync } from 'fs-extra';
|
||||
import { spassr } from 'spassr';
|
||||
import getConfig from '@roxi/routify/lib/utils/config';
|
||||
import autoPreprocess from 'svelte-preprocess';
|
||||
import postcssImport from 'postcss-import';
|
||||
import { injectManifest } from 'rollup-plugin-workbox';
|
||||
|
||||
const { distDir } = getConfig() // use Routify's distDir for SSOT
|
||||
const assetsDir = 'assets'
|
||||
const buildDir = `${distDir}/build`
|
||||
const isNollup = !!process.env.NOLLUP
|
||||
const production = !process.env.ROLLUP_WATCH;
|
||||
|
||||
// clear previous builds
|
||||
removeSync(distDir)
|
||||
removeSync(buildDir)
|
||||
|
||||
const serve = () => ({
|
||||
writeBundle: async () => {
|
||||
const options = {
|
||||
assetsDir: [assetsDir, distDir],
|
||||
entrypoint: `${assetsDir}/__app.html`,
|
||||
script: `${buildDir}/main.js`
|
||||
}
|
||||
spassr({ ...options, port: 5000 })
|
||||
spassr({ ...options, ssr: true, port: 5005, ssrOptions: { inlineDynamicImports: true, dev: true } })
|
||||
}
|
||||
})
|
||||
const copyToDist = () => ({ writeBundle() { copySync(assetsDir, distDir) } })
|
||||
|
||||
|
||||
export default {
|
||||
preserveEntrySignatures: false,
|
||||
input: [`src/main.js`],
|
||||
output: {
|
||||
sourcemap: true,
|
||||
format: 'esm',
|
||||
dir: buildDir,
|
||||
// for performance, disabling filename hashing in development
|
||||
chunkFileNames:`[name]${production && '-[hash]' || ''}.js`
|
||||
},
|
||||
plugins: [
|
||||
svelte({
|
||||
emitCss: false,
|
||||
hot: isNollup,
|
||||
preprocess: [
|
||||
autoPreprocess({
|
||||
postcss: { plugins: [postcssImport()] }
|
||||
})
|
||||
]
|
||||
}),
|
||||
|
||||
// resolve matching modules from current working directory
|
||||
resolve({
|
||||
browser: true,
|
||||
dedupe: importee => !!importee.match(/svelte(\/|$)/)
|
||||
}),
|
||||
commonjs(),
|
||||
|
||||
production && terser(),
|
||||
!production && !isNollup && serve(),
|
||||
!production && !isNollup && livereload(distDir), // refresh entire window when code is updated
|
||||
!production && isNollup && Hmr({ inMemory: true, public: assetsDir, }), // refresh only updated code
|
||||
{
|
||||
// provide node environment on the client
|
||||
transform: code => ({
|
||||
code: code.replace(/process\.env\.NODE_ENV/g, `"${process.env.NODE_ENV}"`),
|
||||
map: { mappings: '' }
|
||||
})
|
||||
},
|
||||
injectManifest({
|
||||
globDirectory: assetsDir,
|
||||
globPatterns: ['**/*.{js,css,svg}', '__app.html'],
|
||||
swSrc: `src/sw.js`,
|
||||
swDest: `${distDir}/serviceworker.js`,
|
||||
maximumFileSizeToCacheInBytes: 10000000, // 10 MB,
|
||||
mode: 'production'
|
||||
}),
|
||||
production && copyToDist(),
|
||||
],
|
||||
watch: {
|
||||
clearScreen: false,
|
||||
buildDelay: 100,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<script>
|
||||
import { Router } from "@roxi/routify";
|
||||
import { routes } from "../.routify/routes";
|
||||
</script>
|
||||
|
||||
<style global>
|
||||
@import "../assets/global.css";
|
||||
</style>
|
||||
|
||||
<Router {routes} />
|
||||
15
src/app.html
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script data-goatcounter="https://mangades.goatcounter.com/count" async src="//gc.zgo.at/count.js"></script>
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="%sveltekit.assets%/global.css" />
|
||||
<link rel="stylesheet" href="%sveltekit.assets%/swiper.min.css" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body>
|
||||
<div id="svelte">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
1
src/global.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/// <reference types="@sveltejs/kit" />
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
<script>
|
||||
import { goto } from '@roxi/routify/runtime/helpers';
|
||||
import autoAnimate from "@formkit/auto-animate";
|
||||
import { goto } from "$app/navigation";
|
||||
import request from "../util/request";
|
||||
|
||||
export var entries;
|
||||
|
|
@ -33,7 +32,7 @@
|
|||
isLoading = false;
|
||||
return
|
||||
}
|
||||
$goto("./" + item.id);
|
||||
goto("./" + item.id);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -43,7 +42,7 @@
|
|||
</dialog>
|
||||
{/if}
|
||||
|
||||
<div class="items" class:items-list={itemsList} use:autoAnimate>
|
||||
<div class="items" class:items-list={itemsList}>
|
||||
{#each entries.sort((a, b) => a.priority - b.priority) as entry}
|
||||
<div class="item" class:r18={entry.media.isAdult} on:click={() => find(entry)}>
|
||||
<div class="flex">
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import request, { imageproxy } from "../util/request";
|
||||
import request, { imageproxy } from "$lib/util/request";
|
||||
|
||||
export var mangaId;
|
||||
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
<script>
|
||||
import { url } from "@roxi/routify/runtime/helpers";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
|
@ -24,6 +23,7 @@
|
|||
dispatch("select", e);
|
||||
}
|
||||
}
|
||||
console.log("ch", chapter);
|
||||
</script>
|
||||
|
||||
<tr class="chapter item" on:mouseenter={mouseenter} on:click={click} class:selected={selected} style="background-image: linear-gradient(to right, rgba(0, 255, 0, 0.247) {progress * 100}%, transparent {progress * 100}%)">
|
||||
|
|
@ -33,7 +33,7 @@
|
|||
<div class="title">{chapter.attributes.title || " "}</div>
|
||||
<div class="scanlation">{scanlationGroup || "Unknown group"}</div>
|
||||
</td>
|
||||
<td class="action no-wrap"><a href={$url("./" + chapter.id + "/1")} on:click|stopPropagation={() => !disabledDownload && dispatch("view")}>View</a></td>
|
||||
<td class="action no-wrap"><a href="./{chapter.id}/1" on:click|stopPropagation={() => !disabledDownload && dispatch("view")}>View</a></td>
|
||||
</tr>
|
||||
|
||||
<style lang="postcss">
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
<script>
|
||||
import SvelteMarkdown from "svelte-markdown";
|
||||
import autoAnimate from "@formkit/auto-animate";
|
||||
import { imageproxy } from "../util/request";
|
||||
import { imageproxy } from "$lib/util/request";
|
||||
|
||||
export var entries;
|
||||
export var itemsList;
|
||||
|
|
@ -12,7 +11,7 @@
|
|||
</script>
|
||||
|
||||
|
||||
<div class="items" class:items-list={itemsList} use:autoAnimate>
|
||||
<div class="items" class:items-list={itemsList}>
|
||||
{#each entries as entry}
|
||||
{@const title = entry.attributes.title.en || entry.attributes.title.ja || Object.values(entry.attributes.title)[0]}
|
||||
<a href="/{entry.id}" class="item" class:r18={!["safe", "suggestive"].includes(entry.attributes.contentRating)} on:click={() => open(entry)}>
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
export function isLogedIn() {
|
||||
if(typeof window === "undefined") return;
|
||||
const token = localStorage.getItem("token");
|
||||
const expiration = new Date(localStorage.getItem("expiration"));
|
||||
|
||||
18
src/lib/util/request.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
export const proxy = "/cors-anywhere/?url="; //"https://cors-anywhere.danbulant.cloud/";
|
||||
export const imageproxy = proxy; // "https://cors-anywhere.danbulant.workers.dev/?";
|
||||
export const base = proxy + encodeURIComponent("https://api.mangadex.org/");
|
||||
export const baseServer = "https://api.mangadex.org/";
|
||||
|
||||
export function getURL(endpoint, query) {
|
||||
if(typeof window === "undefined") return baseServer + endpoint + ((query ? "?" + query.toString() : ""));
|
||||
return base + encodeURIComponent(endpoint + ((query ? "?" + query.toString() : "")));
|
||||
}
|
||||
|
||||
function request(endpoint, query, type = "GET", body) {
|
||||
return fetch(getURL(endpoint, query), {
|
||||
method: type,
|
||||
body: body ? JSON.stringify(body) : undefined
|
||||
}).then(resp => resp.json());
|
||||
}
|
||||
|
||||
export default request;
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
<script>
|
||||
import { redirect } from "@roxi/routify/runtime/helpers";
|
||||
|
||||
$redirect("./1");
|
||||
</script>
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
<script>
|
||||
import { url } from '@roxi/routify'
|
||||
</script>
|
||||
|
||||
<style lang="postcss">
|
||||
.huge {
|
||||
font-size: 12rem;
|
||||
|
|
@ -17,8 +13,9 @@
|
|||
|
||||
<div class="e404">
|
||||
<div class="huge">404</div>
|
||||
<div class="big">Page not found.
|
||||
<!-- link to the parent folder of _fallback.svelte -->
|
||||
<a href={$url('../')}>Go back</a>
|
||||
<div class="big">
|
||||
Page not found.
|
||||
<!-- link to the parent folder of _fallback.svelte -->
|
||||
<a href="/">Go back</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,32 +1,31 @@
|
|||
<script>
|
||||
import { logs } from "../util/logs";
|
||||
import { afterPageLoad } from "@roxi/routify";
|
||||
import { logs } from "$lib/util/logs";
|
||||
|
||||
let skipFirst = true;
|
||||
let last = window.location.pathname;
|
||||
$afterPageLoad(page => {
|
||||
if(skipFirst) return skipFirst = false;
|
||||
if(window.goatcounter) window.goatcounter.count({
|
||||
path: window.location.pathname,
|
||||
title: page.title,
|
||||
referrer: last
|
||||
});
|
||||
else console.warn("Page change; GoatCounter not loaded (yet?)", window.location.pathname);
|
||||
last = window.location.pathname;
|
||||
});
|
||||
// let skipFirst = true;
|
||||
// let last = window.location.pathname;
|
||||
// $afterPageLoad(page => {
|
||||
// if(skipFirst) return skipFirst = false;
|
||||
// if(window.goatcounter) window.goatcounter.count({
|
||||
// path: window.location.pathname,
|
||||
// title: page.title,
|
||||
// referrer: last
|
||||
// });
|
||||
// else console.warn("Page change; GoatCounter not loaded (yet?)", window.location.pathname);
|
||||
// last = window.location.pathname;
|
||||
// });
|
||||
|
||||
let defaultDarkmode = window && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
window && window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
||||
let defaultDarkmode = typeof window !== "undefined" && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
typeof window !== "undefined" && window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
||||
if(defaultDarkmode == darkmode) {
|
||||
darkmode = event.matches;
|
||||
defaultDarkmode = event.matches;
|
||||
}
|
||||
});
|
||||
let darkmode = localStorage && localStorage.getItem("darkmode") || defaultDarkmode;
|
||||
let darkmode = typeof localStorage !== "undefined" && localStorage.getItem("darkmode") || defaultDarkmode;
|
||||
|
||||
$: if(localStorage && darkmode !== defaultDarkmode) localStorage.setItem("darkmode", darkmode);
|
||||
$: if(typeof localStorage !== "undefined" && darkmode !== defaultDarkmode) localStorage.setItem("darkmode", darkmode);
|
||||
|
||||
$: if(darkmode) {
|
||||
$: if(typeof window !== "undefined") if(darkmode) {
|
||||
document.body.classList.add("dark");
|
||||
} else {
|
||||
document.body.classList.remove("dark");
|
||||
|
|
@ -1,16 +1,17 @@
|
|||
<script>
|
||||
import { params } from '@roxi/routify';
|
||||
import request from "../util/request";
|
||||
import { goto } from '@roxi/routify/runtime/helpers';
|
||||
import { getUserDetails, getUserManga, isLogedIn } from "../util/anilist";
|
||||
import AnilistItems from "../components/anilistItems.svelte";
|
||||
import ListOrGrid from "../components/listOrGrid.svelte";
|
||||
import ratelimit from '../util/ratelimit';
|
||||
import MangadexItems from '../components/mangadexItems.svelte';
|
||||
<script lang="ts">
|
||||
import request from "$lib/util/request";
|
||||
import { getUserDetails, getUserManga, isLogedIn } from "$lib/util/anilist";
|
||||
import AnilistItems from "$lib/components/anilistItems.svelte";
|
||||
import ListOrGrid from "$lib/components/listOrGrid.svelte";
|
||||
import ratelimit from '$lib/util/ratelimit';
|
||||
import MangadexItems from '$lib/components/mangadexItems.svelte';
|
||||
import { goto } from "$app/navigation";
|
||||
|
||||
/** @type {string} */
|
||||
var name = $params.search;
|
||||
$: {
|
||||
import type { load } from "./+page";
|
||||
export var data: Awaited<ReturnType<typeof load>>;
|
||||
|
||||
var name: string = data.url.searchParams.get("search") || "";
|
||||
$: if(typeof window !== "undefined") {
|
||||
const url = new URL(window.location.toString());
|
||||
url.searchParams.set("search", name || "");
|
||||
history.replaceState(history.state, "", url.toString());
|
||||
|
|
@ -36,8 +37,8 @@
|
|||
async function search(title, filters, offset=0) {
|
||||
var query = new URLSearchParams();
|
||||
if(title) query.set("title", title);
|
||||
query.set("limit", 100);
|
||||
query.set("offset", offset);
|
||||
query.set("limit", "100");
|
||||
query.set("offset", offset.toString());
|
||||
query.append("includes[]", "author");
|
||||
query.append("includes[]", "cover_art");
|
||||
query.append("includes[]", "artist");
|
||||
|
|
@ -93,7 +94,7 @@
|
|||
async function randomManga() {
|
||||
randomMangaLoading = true;
|
||||
const res = await request("manga/random");
|
||||
$goto("./" + res.data.id);
|
||||
goto("./" + res.data.id);
|
||||
}
|
||||
|
||||
function open() {
|
||||
|
|
@ -105,10 +106,10 @@
|
|||
if(!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(id)) {
|
||||
return alert("You provided invalid ID or link. Make sure you copy the full URL from mangadex.org title page");
|
||||
}
|
||||
$goto("./" + id);
|
||||
goto("./" + id);
|
||||
}
|
||||
|
||||
const anilistID = window.location.hostname === "manga.danbulant.eu" ? "8374" : "8375";
|
||||
const anilistID = data.url.hostname === "manga.danbulant.eu" ? "8374" : "8375";
|
||||
|
||||
let userDetails = isLogedIn() && getUserDetails();
|
||||
let userManga = isLogedIn() && getUserManga();
|
||||
8
src/routes/+page.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export const prerender = true;
|
||||
|
||||
/** @type {import('./$types').PageLoad} */
|
||||
export function load({ url }) {
|
||||
return {
|
||||
url
|
||||
}
|
||||
}
|
||||
17
src/routes/[manga]/+layout.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { error } from '@sveltejs/kit';
|
||||
import request from '$lib/util/request';
|
||||
|
||||
/** @type {import('./$types').PageLoad} */
|
||||
export async function load({ params }) {
|
||||
const blocked = ["227e3f72-863f-46f9-bafe-c43104ca29ee", "b0b721ff-c388-4486-aa0f-c2b0bb321512"];
|
||||
if (blocked.includes(params.manga)) {
|
||||
throw error(404, 'blocked because of copyright');
|
||||
}
|
||||
const manga = await request("manga/" + params.manga + "?includes[]=author&includes[]=cover_art&includes[]=artist");
|
||||
console.log(manga);
|
||||
return {
|
||||
manga: manga.data.attributes,
|
||||
mangaRelationships: manga.data.relationships,
|
||||
id: manga.data.id
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +1,26 @@
|
|||
<script>
|
||||
import { url } from "@roxi/routify/runtime/helpers";
|
||||
import Chapter from "../../components/chapter.svelte";
|
||||
import { EpubGenerator } from "../../util/generateEpub";
|
||||
import { CBZGenerator } from "../../util/generateCbz";
|
||||
import request, { imageproxy } from "../../util/request";
|
||||
import { BaseGenerator } from "../../util/baseGenerator";
|
||||
import { arraysEqual } from "../../util/arrays";
|
||||
import { makeRequest } from "../../util/anilist";
|
||||
import Tabs from "../../components/tabs/tabs.svelte";
|
||||
import Chapter from "$lib/components/chapter.svelte";
|
||||
import { EpubGenerator } from "$lib/util/generateEpub";
|
||||
import { CBZGenerator } from "$lib/util/generateCbz";
|
||||
import request, { imageproxy } from "$lib/util/request";
|
||||
import { BaseGenerator } from "$lib/util/baseGenerator";
|
||||
import { arraysEqual } from "$lib/util/arrays";
|
||||
import { makeRequest } from "$lib/util/anilist";
|
||||
import Tabs from "$lib/components/tabs/tabs.svelte";
|
||||
import { slide } from "svelte/transition";
|
||||
import { Swiper, SwiperSlide } from 'swiper/svelte';
|
||||
import ArtList from "../../components/artList.svelte";
|
||||
import ArtList from "$lib/components/artList.svelte";
|
||||
import SvelteMarkdown from 'svelte-markdown';
|
||||
import ArtDialog from "../../components/artDialog.svelte";
|
||||
import ArtDialog from "$lib/components/artDialog.svelte";
|
||||
|
||||
export var scoped;
|
||||
export var data;
|
||||
|
||||
var mangaId = scoped.id;
|
||||
$: mangaId = scoped.id;
|
||||
var manga = scoped.manga;
|
||||
$: manga = scoped.manga;
|
||||
var relationships = scoped.mangaRelationships;
|
||||
$: relationships = scoped.mangaRelationships;
|
||||
var mangaId = data.id;
|
||||
$: mangaId = data.id;
|
||||
var manga = data.manga;
|
||||
$: manga = data.manga;
|
||||
var relationships = data.mangaRelationships;
|
||||
$: relationships = data.mangaRelationships;
|
||||
var title = manga.title.en || manga.title.jp || Object.values(manga.title)[0];
|
||||
$: title = manga.title.en || manga.title.jp || Object.values(manga.title)[0];
|
||||
|
||||
|
|
@ -261,7 +260,7 @@
|
|||
if(swiper && tabs.indexOf(selectedTab) !== swiper.realIndex) swiper.slideTo(tabs.indexOf(selectedTab));
|
||||
}
|
||||
|
||||
$: chapters && chapters.then(() => swiper.slideToClosest())
|
||||
$: typeof window !== "undefined" && chapters && chapters.then(() => swiper.slideToClosest())
|
||||
|
||||
let swiper;
|
||||
function swiperInit(e) {
|
||||
|
|
@ -326,7 +325,7 @@
|
|||
|
||||
<div class="flex">
|
||||
<div class="linklist">
|
||||
<a href={$url("..")}>Go back to search page</a> <br>
|
||||
<a href="..">Go back to search page</a> <br>
|
||||
<a href="https://mangadex.org/title/{mangaId}">Mangadex.org</a>
|
||||
</div>
|
||||
<div class="copyright-header" class:copyright-header-active={copyrightOpen} on:click={() => copyrightOpen = !copyrightOpen}>Copyright infringement? (click)</div>
|
||||
|
|
@ -435,7 +434,7 @@
|
|||
monochrome: "Monochrome version",
|
||||
adapted_from: "Adapted from",
|
||||
based_on: "Based on",
|
||||
shared_universe: "Shared universe"
|
||||
shared_universe: "Shared universe"
|
||||
}[relatedManga.related] || relatedManga.related}</a> <br>
|
||||
{/each}
|
||||
</div>
|
||||
2
src/routes/[manga]/+page.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export const trailingSlash = 'always';
|
||||
export const ssr = false;
|
||||
21
src/routes/[manga]/[chapter]/+layout.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import request from "$lib/util/request";
|
||||
|
||||
/** @type {import('./$types').LayoutServerLoad} */
|
||||
export async function load({ parent, params }) {
|
||||
const data = await parent();
|
||||
const id = params.chapter;
|
||||
|
||||
const chapterData = (await request("chapter/" + id)).data;
|
||||
console.log("cd", chapterData);
|
||||
|
||||
const atHome = await request("at-home/server/" + id);
|
||||
console.log("@h", data);
|
||||
|
||||
return {
|
||||
manga: data.manga,
|
||||
mangaId: data.id,
|
||||
id,
|
||||
chapter: chapterData,
|
||||
atHome
|
||||
};
|
||||
}
|
||||
8
src/routes/[manga]/[chapter]/+page.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { redirect } from '@sveltejs/kit';
|
||||
|
||||
/** @type {import('./$types').LayoutServerLoad} */
|
||||
export function load() {
|
||||
// throw redirect(301, "./1");
|
||||
}
|
||||
|
||||
export const trailingSlash = "always";
|
||||
|
|
@ -1,19 +1,34 @@
|
|||
<script>
|
||||
import { goto, url } from "@roxi/routify/runtime/helpers";
|
||||
import { imageproxy, proxy } from "../../../util/request";
|
||||
<script lang="ts">
|
||||
import { goto, preloadData } from "$app/navigation";
|
||||
import { imageproxy, proxy } from "$lib/util/request";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
export var page;
|
||||
export var scoped;
|
||||
export var data;
|
||||
|
||||
var chapter = scoped.chapter;
|
||||
$: chapter = scoped.chapter;
|
||||
var manga = scoped.manga;
|
||||
$: manga = scoped.manga;
|
||||
var atHome = scoped.atHome;
|
||||
$: atHome = scoped.atHome;
|
||||
var chapter = data.chapter;
|
||||
$: chapter = data.chapter;
|
||||
var page = data.page;
|
||||
$: page = data.page;
|
||||
var manga = data.manga;
|
||||
$: manga = data.manga;
|
||||
var atHome = data.atHome;
|
||||
$: atHome = data.atHome;
|
||||
var title = manga.title.en || manga.title.jp || Object.values(manga.title)[0];
|
||||
$: title = manga.title.en || manga.title.jp || Object.values(manga.title)[0];
|
||||
|
||||
// $: if(last !== -1) preloadPage(page + 1);
|
||||
|
||||
// onMount(() => preloadPage(page + 1));
|
||||
|
||||
// let last = -1;
|
||||
// function preloadPage(num: number) {
|
||||
// if(last === num) return;
|
||||
// if(typeof window === "undefined") return;
|
||||
// preloadData("./" + num);
|
||||
// (new Image()).src = `${imageproxy}${atHome.baseUrl}/${quality}/${atHome.chapter.hash}/${atHome.chapter[quality][num - 1]}`;
|
||||
// last = num;
|
||||
// }
|
||||
|
||||
var quality = "data";
|
||||
|
||||
var wasArrowUpUp = true;
|
||||
|
|
@ -62,7 +77,7 @@
|
|||
function next() {
|
||||
if(!image.complete) return;
|
||||
if(page > (atHome.chapter[quality].length - 2)) return;
|
||||
$goto("./" + (parseInt(page) + 1));
|
||||
goto("./" + (parseInt(page) + 1));
|
||||
document.scrollingElement.scrollTo({
|
||||
top: 0
|
||||
});
|
||||
|
|
@ -71,7 +86,7 @@
|
|||
function prev() {
|
||||
if(!image.complete) return;
|
||||
if(page < 2) return;
|
||||
$goto("./" + (page - 1));
|
||||
goto("./" + (page - 1));
|
||||
document.scrollingElement.scrollTo({
|
||||
top: 0
|
||||
});
|
||||
|
|
@ -152,7 +167,7 @@
|
|||
};
|
||||
|
||||
//for zoom detection
|
||||
var px_ratio = window.devicePixelRatio || window.screen.availWidth / document.documentElement.clientWidth;
|
||||
var px_ratio = typeof window === "undefined" ? 1 : window.devicePixelRatio || window.screen.availWidth / document.documentElement.clientWidth;
|
||||
|
||||
function isZooming(){
|
||||
var newPx_ratio = window.devicePixelRatio || window.screen.availWidth / document.documentElement.clientWidth;
|
||||
|
|
@ -169,7 +184,7 @@
|
|||
/** @type {HTMLImageElement} */
|
||||
var image;
|
||||
var ratio = 0;
|
||||
$: actualHeight = ratio > 1 ? document.body.clientHeight * ratio : document.body.clientHeight - 17;
|
||||
$: actualHeight = typeof window === "undefined" ? -1 : ratio > 1 ? document.body.clientHeight * ratio : document.body.clientHeight - 17;
|
||||
|
||||
/**
|
||||
* @param {Event} e
|
||||
|
|
@ -193,17 +208,17 @@
|
|||
</svelte:head>
|
||||
|
||||
<div class="top">
|
||||
<a class="back" href={$url("../..")}>Back to chapter list</a>
|
||||
<a class="back" href="../.." data-sveltekit-preload-code="viewport">Back to chapter list</a>
|
||||
</div>
|
||||
|
||||
<img draggable={false} bind:this={image} style="height: {actualHeight}px" on:load={loaded} on:touchstart={handleTouchStart} on:touchmove={handleTouchMove} on:mousedown={mouseclick} on:mouseup={preventDefault} src={`${imageproxy}${atHome.baseUrl}/${quality}/${atHome.chapter.hash}/${atHome.chapter[quality][page - 1]}`} alt="Page {page} in chapter {chapter.attributes.chapter} of {manga.title.en}">
|
||||
|
||||
<div class="bottom">
|
||||
{#if page > 1}
|
||||
<a href={$url("./" + (page - 1))} class="prev">Previous</a>
|
||||
<a href="./{page - 1}" class="prev">Previous</a>
|
||||
{/if}
|
||||
{#if page < atHome.chapter[quality].length - 1}
|
||||
<a href={$url("./" + (parseInt(page) + 1))} class="next">Next</a>
|
||||
<a href="./{parseInt(page) + 1}" class="next">Next</a>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
6
src/routes/[manga]/[chapter]/[page]/+page.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
export function load({ params }) {
|
||||
return {
|
||||
page: params.page
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import request from "../../../util/request";
|
||||
import request from "$lib/util/request";
|
||||
|
||||
export var scoped;
|
||||
export var chapter;
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
<script>
|
||||
import { url } from "@roxi/routify";
|
||||
|
||||
import request from "../../util/request";
|
||||
import request from "$lib/util/request";
|
||||
|
||||
export var manga;
|
||||
|
||||
|
|
@ -23,7 +21,7 @@
|
|||
|
||||
{#if blocked.includes(manga)}
|
||||
<main>
|
||||
<a href={$url("..")}>Search</a>
|
||||
<a href="..">Search</a>
|
||||
<h1>
|
||||
Content blocked.
|
||||
</h1>
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
<script>
|
||||
import { goto } from '@roxi/routify'
|
||||
import { goto } from "$app/navigation";
|
||||
|
||||
let data = new URLSearchParams(window.location.hash.substring(1));
|
||||
console.log(Object.fromEntries([...data.entries()]));
|
||||
|
||||
localStorage.setItem("token", data.get("access_token"));
|
||||
localStorage.setItem("expiration", new Date(Date.now() + Number(data.get("expires_in"))).toISOString());
|
||||
|
||||
$goto("/");
|
||||
goto("/");
|
||||
</script>
|
||||
|
||||
{data}
|
||||
2
src/routes/callback/+page.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export const ssr = false;
|
||||
export const csr = true;
|
||||
19
src/routes/cors-anywhere/+server.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
/** @type {import('./$types').RequestHandler} */
|
||||
export async function GET({ url }) {
|
||||
console.log("[CORS] " + url.searchParams.get('url'));
|
||||
const ret = await fetch(url.searchParams.get('url'));
|
||||
return new Response(await ret.arrayBuffer(), {
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, HEAD, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type, Accept, X-Requested-With',
|
||||
'Access-Control-Max-Age': '86400',
|
||||
|
||||
'Content-Type': ret.headers.get('Content-Type')!,
|
||||
|
||||
'Cache-Control': 'public, max-age=172800', // 2 days
|
||||
'Expires': new Date(Date.now() + 172800000).toUTCString()
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
export const proxy = "https://cors-anywhere.danbulant.cloud/";
|
||||
export const imageproxy = "https://cors-anywhere.danbulant.workers.dev/?";
|
||||
export const base = proxy + "https://api.mangadex.org/";
|
||||
|
||||
function request(endpoint, query, type = "GET", body) {
|
||||
return fetch(base + endpoint + ((query ? "?" + query.toString() : "")), {
|
||||
method: type,
|
||||
body: body ? JSON.stringify(body) : undefined
|
||||
}).then(resp => resp.json());
|
||||
}
|
||||
|
||||
export default request;
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
15
svelte.config.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import adapter from '@sveltejs/adapter-auto';
|
||||
import preprocess from 'svelte-preprocess';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
kit: {
|
||||
adapter: adapter({
|
||||
precompress: true
|
||||
})
|
||||
},
|
||||
|
||||
preprocess: preprocess()
|
||||
};
|
||||
|
||||
export default config;
|
||||
11
tsconfig.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"$lib": ["src/lib"],
|
||||
"$lib/*": ["src/lib/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"],
|
||||
"extends": "./.svelte-kit/tsconfig.json"
|
||||
}
|
||||
17
vercel.json
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"version": 2,
|
||||
"functions": {
|
||||
"api/vercel-ssr/index.js": {
|
||||
"includeFiles": "dist/**"
|
||||
}
|
||||
},
|
||||
"routes": [
|
||||
{
|
||||
"handle": "filesystem"
|
||||
},
|
||||
{
|
||||
"src": "/.*",
|
||||
"dest": "/api/vercel-ssr/index.js"
|
||||
}
|
||||
]
|
||||
}
|
||||
10
vite.config.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import WindiCSS from 'vite-plugin-windicss';
|
||||
|
||||
export default {
|
||||
clearScreen: false,
|
||||
plugins: [
|
||||
WindiCSS(),
|
||||
sveltekit()
|
||||
]
|
||||
}
|
||||