archive for search results
Massimo Melina committed
Jan 18, 2022 at 09:15 UTC
51f03ed57d535b951c9f4610f9a86a945db93cd6
5 files changed
+15
-10
frontend/src/menu.ts
+2
-2
@@ -40,8 +40,8 @@ export function MenuPanel() {
40
h(MenuLink, {
41
icon: 'archive',
42
label: 'Archive',
43
- href: '?get=zip', // @ts-ignore
44
- confirm: 'Download whole folder as ZIP archive?',
43
+ href: '?get=zip' + prefix('&search=', remoteSearch),
44
+ confirm: remoteSearch ? 'Download results of this search as ZIP archive?' : 'Download whole folder as ZIP archive?',
45
})
46
),
47
remoteSearch && h('div', { id: 'searched' }, (stopSearch ? 'Searching' : 'Searched') + ': ' + remoteSearch + prefix(' (', stoppedSearch && 'interrupted', ')')),
src/api.file_list.ts
+3
-5
@@ -1,10 +1,10 @@
1
import { vfs, VfsNode, walkNode } from './vfs'
2
-import _ from 'lodash'
2
import createSSE from './sse'
3
import { basename } from 'path'
4
import { ApiError, ApiHandler } from './apis'
5
import { stat } from 'fs/promises'
6
import { mapPlugins } from './plugins'
7
+import { pattern2filter } from './misc'
8
9
export const file_list:ApiHandler = async ({ path, offset, limit, search, omit, sse }, ctx) => {
10
let node = await vfs.urlToNode(path || '/', ctx)
@@ -16,8 +16,7 @@ export const file_list:ApiHandler = async ({ path, offset, limit, search, omit,
16
return { redirect: path }
17
offset = Number(offset)
18
limit = Number(limit)
19
- const re = new RegExp(_.escapeRegExp(search),'i')
20
- const match = (s?:string) => !s || !search || re.test(s)
19
+ const filter = pattern2filter(search)
20
const walker = walkNode(node, ctx, search ? Infinity : 0)
21
const sseSrv = sse ? createSSE(ctx) : null
22
const onDirEntryHandlers = mapPlugins(plug => plug.onDirEntry)
@@ -28,8 +27,7 @@ export const file_list:ApiHandler = async ({ path, offset, limit, search, omit,
27
const list = []
28
for await (const sub of walker) {
29
if (sseSrv?.stopped || ctx.aborted) break
31
- const filename = basename(sub.name||'')
32
- if (!match(filename))
30
+ if (!filter(sub.name))
31
continue
32
const entry = await nodeToDirEntry(sub)
33
if (!entry)
src/misc.ts
+7
@@ -2,6 +2,7 @@ import { EventEmitter } from 'events'
2
import fs from 'fs/promises'
3
import { basename, dirname } from 'path'
4
import { watch } from 'fs'
5
+import _ from 'lodash'
6
7
export function enforceFinal(sub:string, s:string) {
8
return s.endsWith(sub) ? s : s+sub
@@ -97,3 +98,9 @@ export function watchDir(dir: string, cb: ()=>void) {
98
try { watch(dir, cb) }
99
catch {}
100
}
101
+
102
+export function pattern2filter(pattern: string){
103
+ const re = new RegExp(_.escapeRegExp(pattern), 'i')
104
+ return (s?:string) =>
105
+ !s || !pattern || re.test(basename(s))
106
+}
src/zip.ts
+3
-2
@@ -1,6 +1,6 @@
1
import { VfsNode, walkNode } from './vfs'
2
import Koa from 'koa'
3
-import { filterMapGenerator } from './misc'
3
+import { filterMapGenerator, pattern2filter } from './misc'
4
import { QuickZipStream } from './QuickZipStream'
5
import { createReadStream } from 'fs'
6
import fs from 'fs/promises'
@@ -11,8 +11,9 @@ export async function zipStreamFromFolder(node: VfsNode, ctx: Koa.Context) {
11
ctx.mime = 'zip'
12
const { name } = node
13
ctx.attachment((name || 'archive') + '.zip')
14
+ const filter = pattern2filter(String(ctx.query.search||''))
15
const walker = filterMapGenerator(walkNode(node, ctx, Infinity), async (el:VfsNode) => {
15
- if (!el.source || ctx.req.aborted)
16
+ if (!el.source || ctx.req.aborted || !filter(el.name))
17
return
18
try {
19
const st = await fs.stat(el.source)
todo.md
-1
@@ -15,7 +15,6 @@
15
- config: bans
16
- config: min disk space
17
- thumbnails support
18
-- archive for search results
18
- archive only selected files
19
- "bottom" button (only on long screens, only after you scroll a bit, only for a few seconds)
20
- webdav?