mirror of
https://github.com/tachiyomiorg/tachiyomi-extensions-inspector.git
synced 2025-01-23 22:21:09 +01:00
add library
This commit is contained in:
parent
eb90db7ce6
commit
09d624a4e2
@ -6,11 +6,13 @@ package ir.armor.tachidesk
|
|||||||
|
|
||||||
import eu.kanade.tachiyomi.App
|
import eu.kanade.tachiyomi.App
|
||||||
import io.javalin.Javalin
|
import io.javalin.Javalin
|
||||||
|
import ir.armor.tachidesk.util.addMangaToLibrary
|
||||||
import ir.armor.tachidesk.util.applicationSetup
|
import ir.armor.tachidesk.util.applicationSetup
|
||||||
import ir.armor.tachidesk.util.getChapter
|
import ir.armor.tachidesk.util.getChapter
|
||||||
import ir.armor.tachidesk.util.getChapterList
|
import ir.armor.tachidesk.util.getChapterList
|
||||||
import ir.armor.tachidesk.util.getExtensionIcon
|
import ir.armor.tachidesk.util.getExtensionIcon
|
||||||
import ir.armor.tachidesk.util.getExtensionList
|
import ir.armor.tachidesk.util.getExtensionList
|
||||||
|
import ir.armor.tachidesk.util.getLibraryMangas
|
||||||
import ir.armor.tachidesk.util.getManga
|
import ir.armor.tachidesk.util.getManga
|
||||||
import ir.armor.tachidesk.util.getMangaList
|
import ir.armor.tachidesk.util.getMangaList
|
||||||
import ir.armor.tachidesk.util.getPageImage
|
import ir.armor.tachidesk.util.getPageImage
|
||||||
@ -20,6 +22,7 @@ import ir.armor.tachidesk.util.getThumbnail
|
|||||||
import ir.armor.tachidesk.util.installAPK
|
import ir.armor.tachidesk.util.installAPK
|
||||||
import ir.armor.tachidesk.util.openInBrowser
|
import ir.armor.tachidesk.util.openInBrowser
|
||||||
import ir.armor.tachidesk.util.removeExtension
|
import ir.armor.tachidesk.util.removeExtension
|
||||||
|
import ir.armor.tachidesk.util.removeMangaFromLibrary
|
||||||
import ir.armor.tachidesk.util.sourceFilters
|
import ir.armor.tachidesk.util.sourceFilters
|
||||||
import ir.armor.tachidesk.util.sourceGlobalSearch
|
import ir.armor.tachidesk.util.sourceGlobalSearch
|
||||||
import ir.armor.tachidesk.util.sourceSearch
|
import ir.armor.tachidesk.util.sourceSearch
|
||||||
@ -73,15 +76,16 @@ class Main {
|
|||||||
println("Warning: react build files are missing.")
|
println("Warning: react build files are missing.")
|
||||||
hasWebUiBundled = false
|
hasWebUiBundled = false
|
||||||
}
|
}
|
||||||
|
config.enableCorsForAllOrigins()
|
||||||
}.start(4567)
|
}.start(4567)
|
||||||
if (hasWebUiBundled) {
|
if (hasWebUiBundled) {
|
||||||
openInBrowser()
|
openInBrowser()
|
||||||
}
|
}
|
||||||
|
|
||||||
app.before() { ctx ->
|
// app.before() { ctx ->
|
||||||
// allow the client which is running on another port
|
// // allow the client which is running on another port
|
||||||
ctx.header("Access-Control-Allow-Origin", "*")
|
// ctx.header("Access-Control-Allow-Origin", "*")
|
||||||
}
|
// }
|
||||||
|
|
||||||
app.get("/api/v1/extension/list") { ctx ->
|
app.get("/api/v1/extension/list") { ctx ->
|
||||||
ctx.json(getExtensionList())
|
ctx.json(getExtensionList())
|
||||||
@ -146,12 +150,17 @@ class Main {
|
|||||||
|
|
||||||
// adds the manga to library
|
// adds the manga to library
|
||||||
app.get("api/v1/manga/:mangaId/library") { ctx ->
|
app.get("api/v1/manga/:mangaId/library") { ctx ->
|
||||||
// TODO
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
||||||
|
addMangaToLibrary(mangaId)
|
||||||
|
ctx.status(200)
|
||||||
}
|
}
|
||||||
|
|
||||||
// removes the manga from the library
|
// removes the manga from the library
|
||||||
app.delete("api/v1/manga/:mangaId/library") { ctx ->
|
app.delete("api/v1/manga/:mangaId/library") { ctx ->
|
||||||
// TODO
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
||||||
|
println("fuck")
|
||||||
|
removeMangaFromLibrary(mangaId)
|
||||||
|
ctx.status(200)
|
||||||
}
|
}
|
||||||
|
|
||||||
// adds the manga to category
|
// adds the manga to category
|
||||||
@ -207,7 +216,7 @@ class Main {
|
|||||||
|
|
||||||
// lists all manga in the library, suitable if no categories are defined
|
// lists all manga in the library, suitable if no categories are defined
|
||||||
app.get("/api/v1/library/") { ctx ->
|
app.get("/api/v1/library/") { ctx ->
|
||||||
// TODO
|
ctx.json(getLibraryMangas())
|
||||||
}
|
}
|
||||||
|
|
||||||
// category list
|
// category list
|
||||||
|
@ -20,7 +20,8 @@ data class MangaDataClass(
|
|||||||
val author: String? = null,
|
val author: String? = null,
|
||||||
val description: String? = null,
|
val description: String? = null,
|
||||||
val genre: String? = null,
|
val genre: String? = null,
|
||||||
val status: String = MangaStatus.UNKNOWN.name
|
val status: String = MangaStatus.UNKNOWN.name,
|
||||||
|
val inLibrary: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
||||||
data class PagedMangaListDataClass(
|
data class PagedMangaListDataClass(
|
||||||
|
@ -5,7 +5,10 @@ package ir.armor.tachidesk.database.table
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
|
import ir.armor.tachidesk.database.dataclass.MangaDataClass
|
||||||
|
import ir.armor.tachidesk.util.proxyThumbnailUrl
|
||||||
import org.jetbrains.exposed.dao.id.IntIdTable
|
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||||
|
import org.jetbrains.exposed.sql.ResultRow
|
||||||
|
|
||||||
object MangaTable : IntIdTable() {
|
object MangaTable : IntIdTable() {
|
||||||
val url = varchar("url", 2048)
|
val url = varchar("url", 2048)
|
||||||
@ -27,6 +30,25 @@ object MangaTable : IntIdTable() {
|
|||||||
val sourceReference = reference("source", SourceTable)
|
val sourceReference = reference("source", SourceTable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun MangaTable.toDataClass(mangaEntry: ResultRow) =
|
||||||
|
MangaDataClass(
|
||||||
|
mangaEntry[MangaTable.id].value,
|
||||||
|
mangaEntry[sourceReference].value,
|
||||||
|
|
||||||
|
mangaEntry[MangaTable.url],
|
||||||
|
mangaEntry[MangaTable.title],
|
||||||
|
proxyThumbnailUrl(mangaEntry[MangaTable.id].value),
|
||||||
|
|
||||||
|
mangaEntry[MangaTable.initialized],
|
||||||
|
|
||||||
|
mangaEntry[MangaTable.artist],
|
||||||
|
mangaEntry[MangaTable.author],
|
||||||
|
mangaEntry[MangaTable.description],
|
||||||
|
mangaEntry[MangaTable.genre],
|
||||||
|
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
|
||||||
|
mangaEntry[MangaTable.inLibrary]
|
||||||
|
)
|
||||||
|
|
||||||
enum class MangaStatus(val status: Int) {
|
enum class MangaStatus(val status: Int) {
|
||||||
UNKNOWN(0),
|
UNKNOWN(0),
|
||||||
ONGOING(1),
|
ONGOING(1),
|
||||||
|
42
server/src/main/kotlin/ir/armor/tachidesk/util/Library.kt
Normal file
42
server/src/main/kotlin/ir/armor/tachidesk/util/Library.kt
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
|
import ir.armor.tachidesk.database.dataclass.MangaDataClass
|
||||||
|
import ir.armor.tachidesk.database.table.MangaTable
|
||||||
|
import ir.armor.tachidesk.database.table.toDataClass
|
||||||
|
import org.jetbrains.exposed.sql.select
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
import org.jetbrains.exposed.sql.update
|
||||||
|
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
fun addMangaToLibrary(mangaId: Int) {
|
||||||
|
val manga = getManga(mangaId)
|
||||||
|
if (!manga.inLibrary) {
|
||||||
|
transaction {
|
||||||
|
MangaTable.update({ MangaTable.id eq manga.id }) {
|
||||||
|
it[inLibrary] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeMangaFromLibrary(mangaId: Int) {
|
||||||
|
val manga = getManga(mangaId)
|
||||||
|
if (!manga.inLibrary) {
|
||||||
|
transaction {
|
||||||
|
MangaTable.update({ MangaTable.id eq manga.id }) {
|
||||||
|
it[inLibrary] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLibraryMangas(): List<MangaDataClass> {
|
||||||
|
return transaction {
|
||||||
|
MangaTable.select { MangaTable.inLibrary eq true }.map {
|
||||||
|
MangaTable.toDataClass(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -34,6 +34,7 @@ fun getManga(mangaId: Int, proxyThumbnail: Boolean = true): MangaDataClass {
|
|||||||
mangaEntry[MangaTable.description],
|
mangaEntry[MangaTable.description],
|
||||||
mangaEntry[MangaTable.genre],
|
mangaEntry[MangaTable.genre],
|
||||||
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
|
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
|
||||||
|
mangaEntry[MangaTable.inLibrary]
|
||||||
)
|
)
|
||||||
} else { // initialize manga
|
} else { // initialize manga
|
||||||
val source = getHttpSource(mangaEntry[MangaTable.sourceReference].value)
|
val source = getHttpSource(mangaEntry[MangaTable.sourceReference].value)
|
||||||
@ -77,6 +78,7 @@ fun getManga(mangaId: Int, proxyThumbnail: Boolean = true): MangaDataClass {
|
|||||||
fetchedManga.description,
|
fetchedManga.description,
|
||||||
fetchedManga.genre,
|
fetchedManga.genre,
|
||||||
MangaStatus.valueOf(fetchedManga.status).name,
|
MangaStatus.valueOf(fetchedManga.status).name,
|
||||||
|
false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ fun MangasPage.processEntries(sourceId: Long): PagedMangaListDataClass {
|
|||||||
manga.author,
|
manga.author,
|
||||||
manga.description,
|
manga.description,
|
||||||
manga.genre,
|
manga.genre,
|
||||||
MangaStatus.valueOf(manga.status).name,
|
MangaStatus.valueOf(manga.status).name
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
val mangaId = mangaEntry[MangaTable.id].value
|
val mangaId = mangaEntry[MangaTable.id].value
|
||||||
@ -83,6 +83,7 @@ fun MangasPage.processEntries(sourceId: Long): PagedMangaListDataClass {
|
|||||||
mangaEntry[MangaTable.description],
|
mangaEntry[MangaTable.description],
|
||||||
mangaEntry[MangaTable.genre],
|
mangaEntry[MangaTable.genre],
|
||||||
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
|
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
|
||||||
|
mangaEntry[MangaTable.inLibrary]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
webUI/node_modules/.yarn-integrity
generated
vendored
10
webUI/node_modules/.yarn-integrity
generated
vendored
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"systemParams": "linux-x64-88",
|
|
||||||
"modulesFolders": [],
|
|
||||||
"flags": [],
|
|
||||||
"linkedModules": [],
|
|
||||||
"topLevelPatterns": [],
|
|
||||||
"lockfileEntries": {},
|
|
||||||
"files": [],
|
|
||||||
"artifacts": {}
|
|
||||||
}
|
|
@ -20,6 +20,7 @@ import Reader from './screens/Reader';
|
|||||||
import Search from './screens/SearchSingle';
|
import Search from './screens/SearchSingle';
|
||||||
import NavBarTitle from './context/NavbarTitle';
|
import NavBarTitle from './context/NavbarTitle';
|
||||||
import DarkTheme from './context/DarkTheme';
|
import DarkTheme from './context/DarkTheme';
|
||||||
|
import Library from './screens/Library';
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [title, setTitle] = useState<string>('Tachidesk');
|
const [title, setTitle] = useState<string>('Tachidesk');
|
||||||
@ -53,7 +54,6 @@ export default function App() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
|
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<NavBarTitle.Provider value={navTitleContext}>
|
<NavBarTitle.Provider value={navTitleContext}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
@ -83,6 +83,9 @@ export default function App() {
|
|||||||
<Route path="/manga/:id">
|
<Route path="/manga/:id">
|
||||||
<Manga />
|
<Manga />
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path="/library">
|
||||||
|
<Library />
|
||||||
|
</Route>
|
||||||
<Route path="/">
|
<Route path="/">
|
||||||
<Home />
|
<Home />
|
||||||
</Route>
|
</Route>
|
||||||
|
@ -2,20 +2,49 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import React from 'react';
|
import { Button } from '@material-ui/core';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
interface IProps{
|
interface IProps{
|
||||||
manga: IManga | undefined
|
manga: IManga
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MangaDetails(props: IProps) {
|
export default function MangaDetails(props: IProps) {
|
||||||
const { manga } = props;
|
const { manga } = props;
|
||||||
|
const [inLibrary, setInLibrary] = useState<string>(
|
||||||
|
manga.inLibrary ? 'In Library' : 'Not In Library',
|
||||||
|
);
|
||||||
|
|
||||||
|
function addToLibrary() {
|
||||||
|
setInLibrary('adding');
|
||||||
|
fetch(`http://127.0.0.1:4567/api/v1/manga/${manga.id}/library/`).then(() => {
|
||||||
|
setInLibrary('In Library');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeFromLibrary() {
|
||||||
|
setInLibrary('removing');
|
||||||
|
fetch(`http://127.0.0.1:4567/api/v1/manga/${manga.id}/library/`, { method: 'DELETE', mode: 'cors' }).then(() => {
|
||||||
|
setInLibrary('Not In Library');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleButtonClick() {
|
||||||
|
if (inLibrary === 'Not In Library') {
|
||||||
|
addToLibrary();
|
||||||
|
} else {
|
||||||
|
removeFromLibrary();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h1>
|
<h1>
|
||||||
{manga && manga.title}
|
{manga && manga.title}
|
||||||
</h1>
|
</h1>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'row-reverse' }}>
|
||||||
|
<Button variant="outlined" onClick={() => handleButtonClick()}>{inLibrary}</Button>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,14 @@ export default function TemporaryDrawer({ drawerOpen, setDrawerOpen }: IProps) {
|
|||||||
onKeyDown={() => setDrawerOpen(false)}
|
onKeyDown={() => setDrawerOpen(false)}
|
||||||
>
|
>
|
||||||
<List>
|
<List>
|
||||||
|
<Link to="/library" style={{ color: 'inherit', textDecoration: 'none' }}>
|
||||||
|
<ListItem button key="Library">
|
||||||
|
<ListItemIcon>
|
||||||
|
<InboxIcon />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary="Library" />
|
||||||
|
</ListItem>
|
||||||
|
</Link>
|
||||||
<Link to="/extensions" style={{ color: 'inherit', textDecoration: 'none' }}>
|
<Link to="/extensions" style={{ color: 'inherit', textDecoration: 'none' }}>
|
||||||
<ListItem button key="Extensions">
|
<ListItem button key="Extensions">
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
|
34
webUI/react/src/screens/Library.tsx
Normal file
34
webUI/react/src/screens/Library.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
|
import MangaGrid from '../components/MangaGrid';
|
||||||
|
import NavBarTitle from '../context/NavbarTitle';
|
||||||
|
|
||||||
|
export default function MangaList() {
|
||||||
|
const { setTitle } = useContext(NavBarTitle);
|
||||||
|
const [mangas, setMangas] = useState<IManga[]>([]);
|
||||||
|
const [lastPageNum, setLastPageNum] = useState<number>(1);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTitle('Library');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('http://127.0.0.1:4567/api/v1/library')
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data: IManga[]) => {
|
||||||
|
setMangas(data);
|
||||||
|
});
|
||||||
|
}, [lastPageNum]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MangaGrid
|
||||||
|
mangas={mangas}
|
||||||
|
hasNextPage={false}
|
||||||
|
lastPageNum={lastPageNum}
|
||||||
|
setLastPageNum={setLastPageNum}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -38,7 +38,7 @@ export default function Manga() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MangaDetails manga={manga} />
|
{manga && <MangaDetails manga={manga} />}
|
||||||
{chapterCards}
|
{chapterCards}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
1
webUI/react/src/typings.d.ts
vendored
1
webUI/react/src/typings.d.ts
vendored
@ -25,6 +25,7 @@ interface IManga {
|
|||||||
id: number
|
id: number
|
||||||
title: string
|
title: string
|
||||||
thumbnailUrl: string
|
thumbnailUrl: string
|
||||||
|
inLibrary?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IChapter {
|
interface IChapter {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user