Skip to content

Commit c0aeb6d

Browse files
committed
improve job handling
1 parent eb5fe37 commit c0aeb6d

File tree

4 files changed

+61
-23
lines changed

4 files changed

+61
-23
lines changed

app/components/JobList.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DownloadPayload } from "@/lib/download-queue.server"
2-
import { Link, useLoaderData, useNavigation, useRevalidator } from "@remix-run/react"
2+
import { Form, Link, useLoaderData, useNavigation, useRevalidator } from "@remix-run/react"
33
import { Job } from "bullmq"
44
import { IconCheck, IconClose, IconLoading } from "./icons"
55
import { loader } from '@/routes/_index'
@@ -42,9 +42,23 @@ export default function JobList() {
4242
return (
4343
_jobs.length > 0 && (
4444
<div className="mt-8">
45-
<h2 className="text-xl font-medium px-3 mb-2">
46-
Recent downloads
47-
</h2>
45+
<div className="px-3 flex items-center">
46+
<h2 className="flex-grow text-xl font-medium mb-2">
47+
Recent downloads
48+
</h2>
49+
<Form className='inline' method='POST'>
50+
<button
51+
className='flex items-center gap-2 px-2 py-1 border rounded-md hover:bg-gray-50 transition-colors'
52+
type="submit"
53+
name="_action"
54+
value="clear"
55+
disabled={busy}
56+
>
57+
<IconClose />
58+
<p>Clear</p>
59+
</button>
60+
</Form>
61+
</div>
4862
<ul className="divide-y">
4963
{_jobs.map((job) => (
5064
<li key={job.id} className="p-4 flex items-center gap-2 hover:bg-gray-100 transition-colors">

app/lib/download-queue.server.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { getJSON } from "@/request"
22
import { registerQueue } from "./queue.server"
33
import { BASE_URL, IMAGE_PREFIX } from "@/config"
44
import fs from 'fs/promises'
5-
import { ImageProps } from "@/components/Image"
65
import path from 'path'
76
import JSZip from "jszip"
87
import { Queue } from "bullmq"
@@ -34,19 +33,24 @@ export type DownloadMeta = {
3433
type Chapter = {
3534
seoTitle: string
3635
chapter: {
37-
md_images: ImageProps[]
36+
images: { url: string; w: number; h: number }[]
3837
}
3938
}
4039

4140
export const downloadQueue = registerQueue<DownloadPayload>('download', async (job) => {
4241
const id = job.data.chapter_id
4342
const comic_title = job.data.meta.comic_title
44-
const data = await getJSON<Chapter>(`${BASE_URL}/chapter/${id}`)
43+
const data = await getJSON<Chapter>(`${BASE_URL}/chapter/${id}?tachiyomi=true`)
4544

46-
const urls = data.chapter.md_images.map((image) => ({
47-
url: `${IMAGE_PREFIX}/${image.b2key}`,
48-
key: image.b2key
49-
}))
45+
console.log('received data: \n', data.chapter.images)
46+
47+
const urls = data.chapter.images.map((image) => {
48+
const key = new URL(image.url).pathname.slice(1)
49+
return {
50+
url: `${IMAGE_PREFIX}/${key}?tachiyomi=true`,
51+
key
52+
}
53+
})
5054

5155
job.updateProgress(0)
5256

app/routes/_index.tsx

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type SearchResult = {
2828

2929
export async function loader({ request }: LoaderFunctionArgs) {
3030
const q = new URL(request.url).searchParams.get("q")
31-
const url = `${BASE_URL}/v1.0/search?q=${q}`
31+
const url = `${BASE_URL}/v1.0/search?q=${q}&tachiyomi=true`
3232
const recent = await getRecentQueries()
3333
const files = await getFiles()
3434
const jobs = await downloadQueue.getJobs()
@@ -61,6 +61,10 @@ export async function action({ request }: ActionFunctionArgs) {
6161
if (action === 'scan') {
6262
await scanQueue.add('scan', { path: STORAGE_PATH })
6363
}
64+
if (action === 'clear') {
65+
await downloadQueue.clean(30 * 1000, 5000, 'failed')
66+
await downloadQueue.clean(30 * 1000, 5000, 'completed')
67+
}
6468

6569
return { action }
6670
}
@@ -75,7 +79,7 @@ export default function Index() {
7579
const busy = state !== "idle"
7680

7781
return (
78-
<main className="max-w-screen-lg mx-auto p-4">
82+
<main className="max-w-screen-lg mx-auto py-4 px-2">
7983
<h1 className="text-center text-2xl my-4">
8084
Comick Downloader
8185
</h1>
@@ -121,15 +125,26 @@ export default function Index() {
121125
)}
122126
{results.length > 0 && !busy && (
123127
<div className="mt-8">
124-
<h2 className="text-xl font-medium px-2">
128+
<h2 className="text-2xl mb-2">
125129
Results
126130
</h2>
127-
<ul className="divide-y">
131+
<ul>
128132
{results.map((result) => (
129-
<li key={result.hid} className="p-4 my-2 flex items-stretch gap-2 hover:bg-gray-100 transition-colors">
130-
<Image w={100} h={100} b2key={result.md_covers[0]?.b2key} />
131-
<Link to={`/comic/${result.hid}`} className="flex-grow text-lg font-semibold">{result.title}</Link>
132-
<a href={`https://comick.io/comic/${result.slug}`}>Link</a>
133+
<li
134+
key={result.hid}
135+
className="border-t border-gray-300 cursor-pointer relative pb-3 flex items-stretch gap-2 hover:bg-gray-100 transition-colors"
136+
>
137+
<Link
138+
to={`/comic/${result.hid}`}
139+
className="absolute inset-0 w-full h-full"
140+
>
141+
<span className="hidden">{result.title}</span>
142+
</Link>
143+
<Image w={120} h={120} b2key={result.md_covers[0]?.b2key} />
144+
<div className="flex-grow">
145+
<p className="pt-1 text-lg font-semibold">{result.title}</p>
146+
<a className="hover:underline text-xs" href={`https://comick.io/comic/${result.slug}`}>source</a>
147+
</div>
133148
</li>
134149
))}
135150
</ul>
@@ -147,7 +162,7 @@ function FSInfo() {
147162
const { state } = useNavigation()
148163
const busy = state !== "idle"
149164

150-
console.log(files)
165+
console.log('Filesystem files: ', files)
151166

152167
return (
153168
<div className="mt-8">

app/routes/comic.$id.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
5252

5353
const [jobs, comic, chapters] = await Promise.all([
5454
downloadQueue.getJobs(),
55-
getJSON<Comic>(`${BASE_URL}/comic/${id}`),
55+
getJSON<Comic>(`${BASE_URL}/comic/${id}?tachiyomi=true`),
5656
getJSON<{ chapters: FullChapter[] }>(`${BASE_URL}/comic/${id}/chapters?${remoteSP.toString()}`)
5757
.then((res) => res.chapters),
5858
])
@@ -70,9 +70,14 @@ export async function action({ request }: ActionFunctionArgs) {
7070
downloadQueue.add(
7171
`Download chapter ${id}`,
7272
payload,
73-
{ removeOnComplete: 1000, removeOnFail: 5000 }
73+
{ removeOnComplete: 1000, removeOnFail: 5000, attempts: 3, backoff: { type: 'exponential', delay: 1000 } }
7474
)
7575
}
76+
if (action === 'retry') {
77+
const jobs = await downloadQueue.getJobs('failed')
78+
const job = jobs.find((j) => j.data.chapter_id === id)
79+
await job?.retry()
80+
}
7681

7782
return null
7883
}
@@ -323,7 +328,7 @@ function ChapterList() {
323328
title={getTooltip(c.hid)}
324329
type='submit'
325330
name='_action'
326-
value='download'
331+
value={isError(c.hid) ? 'retry' : 'download'}
327332
disabled={busy && isPost}
328333
>
329334
{getIcon(c.hid)}

0 commit comments

Comments
 (0)