plugin/download-counter: Count files in zip/archives (optional)
Massimo Melina committed
Aug 2, 2023 at 17:43 UTC
9c71218ec96184197426126c9d89ab8490d04048
4 files changed
+28
-7
plugins/download-counter/plugin.js
+11
-6
@@ -1,13 +1,14 @@
1
// other plugins can use ctx.state.download_counter_ignore to mark downloads that shouldn't be counted
2
3
exports.description = "Counts downloads for each file, and displays the total in the list or file menu"
4
-exports.version = 4.1 // fix: different cases and encodings with urls weren't properly counted
4
+exports.version = 5 // count files in archive
5
exports.apiRequired = 8.3
6
7
exports.config = {
8
where: { frontend: true, type: 'select', defaultValue: 'menu',
9
options: ['list', { value: 'menu', label: "file menu" }],
10
- }
10
+ },
11
+ archives: { defaultValue: true, type: 'boolean', label: "Count files in zip/archives" },
12
}
13
exports.configDialog = {
14
sx: { maxWidth: '20em' },
@@ -45,11 +46,15 @@ exports.init = async api => {
46
unload: () => save.flush(), // we may have pending savings
47
middleware: (ctx) =>
48
() => { // execute after other middlewares are done
48
- if (ctx.status >= 300 || !ctx.vfsNode || ctx.state.download_counter_ignore) return
49
- if (ctx.state.includesLastByte === false) return
49
+ if (ctx.status >= 300 || ctx.state.download_counter_ignore || ctx.state.includesLastByte === false) return
50
+ if (!(ctx.vfsNode || api.getConfig('archives') && ctx.state.archive)) return
51
ctx.state.completed.then(() => {
51
- const k = uri2key(ctx.path)
52
- counters[k] = counters[k] + 1 || 1
52
+ const key = uri2key(ctx.path)
53
+ const entries = ctx.vfsNode ? [key]
54
+ : ctx.state.originalStream?.getArchiveEntries?.().filter(x => x.at(-1) !== '/').map(x => key + uri2key(x))
55
+ if (!entries) return
56
+ for (const k of entries)
57
+ counters[k] = counters[k] + 1 || 1
58
save()
59
})
60
},
plugins/vhosting/plugin.js
+2
@@ -44,6 +44,8 @@ exports.init = api => {
44
let { root='' } = row
45
if (root.endsWith('/'))
46
root = root.slice(0, -1)
47
+ if (root && root[0] !== '/') // normalize
48
+ root = '/' + root
49
if (!root) return
50
if (!params)
51
ctx.path = root + ctx.path
src/QuickZipStream.ts
+14
-1
@@ -24,10 +24,19 @@ interface ZipSource {
24
ts?: Date
25
mode?: number
26
}
27
+interface QuickZipEntry {
28
+ size: number,
29
+ crc: number,
30
+ ts: Date,
31
+ pathAsBuffer: Buffer,
32
+ offset: number,
33
+ version: number,
34
+ extAttr: number,
35
+}
36
export class QuickZipStream extends Readable {
37
private workingFile: Readable | undefined
38
private finished = false
30
- private readonly entries: ({ size:number, crc:number, ts:Date, pathAsBuffer:Buffer, offset:number, version:number, extAttr: number })[] = []
39
+ private readonly entries: QuickZipEntry[] = []
40
private dataWritten = 0
41
private consumedCalculating: ZipSource[] = []
42
private skip: number = 0
@@ -38,6 +47,10 @@ export class QuickZipStream extends Readable {
47
super({})
48
}
49
50
+ getArchiveEntries() {
51
+ return this.entries.map(x => String(x.pathAsBuffer))
52
+ }
53
+
54
earlyClose() {
55
this.finished = true
56
this.push(null)
src/throttler.ts
+1
@@ -76,6 +76,7 @@ export const throttler: Koa.Middleware = async (ctx, next) => {
76
})
77
78
const downloadTotal: number = ctx.response.length
79
+ ctx.state.originalStream = ctx.body
80
ctx.body = ctx.body.pipe(ts)
81
82
if (downloadTotal) // preserve this info