Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(local): rewrite the image selection, fix #102 #110

Merged
merged 10 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 36 additions & 15 deletions docs/zh-CN/plugins/local.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,29 @@

图源文件夹,支持多个不同的文件夹

#### languages
#### storage

- 类型: `string[]`
- 默认值: `['zh-CN', 'en']`
- 类型: `ENUM`
- 选项: `file | database`
- 默认值: `file`

图源支持的语言
图源存数据存储方式,`file` 为文件存储,`database` 为数据库存储

#### extension
#### reload

- 类型: `boolean`
- 默认值: `false`

是否在每次启动时重新扫描图源文件夹

#### languages

- 类型: `string[]`
- 默认值: `['.jpg', '.png', '.jpeg', '.gif']`
- 默认值: `['zh-CN']`

支持的图片扩展名
图源支持的语言

### 文件设置

#### scraper

Expand All @@ -45,6 +55,13 @@

文件元信息刮削器格式,详见 [刮削器](#刮削器)

#### extension

- 类型: `string[]`
- 默认值: `['.jpg', '.png', '.jpeg', '.gif']`

支持的图片扩展名,请注意扩展名前的 `.` 是必须的

## 刮削器

:::tip
Expand All @@ -55,41 +72,45 @@

### 使用

插件设置中 `scraper` 默认值可得出大致的使用方式
插件设置中 `scraper` 默认值可得出大致的使用方式:当 `scraper` 为 `{filename}-{tag}` 时,文件名为 `foo-[bar].jpg` 的图片将被刮削为 `{name: 'foo', tag: ['bar'], ...}`。

### 标签
即:文件名为 `foo` 的图片,其拥有 `bar` 这个 tags。

### 语法

#### `#...#`

- 类型: `string`
- 默认值: `name`
- 示例: `#name#{fliename}-{tag}`

(仅在开头有效)指定刮削器的工作方式,目前支持以下几种方式:
> 该语法仅在 `scraper` 的第一个元素中有效,否则将被忽略

指定刮削器的工作方式,目前支持以下几种方式:

1. `name`: 文件名模式
2. `meta`(WIP): 文件元信息模式(开发中)
2. `meta`: 文件元信息模式(开发中)

#### `{filename}`

- 类型: `string`

> 当 `{filename}` 被放置在最后时,`+` 将失效( `{foo}-{filename}+`)
> 当 `{filename}` 被放置在最后时,`+` 将失效(e.g. `{foo}-{filename}+`)

文件名
指示文件名所在的位置,文件名将被刮削为 `name`,并作为图片的 `name` 属性

#### `{tag}`

- 类型: `string[]`

图片拥有的 tag
指示标签所在的位置,标签将被刮削为 `tag`,并作为图片的 `tags` 属性

#### `{nsfw}`(WIP)

- 类型: `boolean | 'furry' | 'guro' | 'shota' | 'bl'`
- 默认值: `nsfw=false`

限制级图片
指示图片是否为 nsfw,若为 `boolean` 类型,则直接将其作为 `nsfw` 属性,若为 `string` 类型,则将其作为 `nsfw` 属性的值

#### `+`

Expand Down
45 changes: 37 additions & 8 deletions packages/local/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ declare module 'koishi' {
}

class LocalImageSource extends ImageSource<LocalImageSource.Config> {
static override inject = {
required: ['booru'],
optional: ['database', 'cache']
}
languages = []
source = 'local'
private imageMap: LocalStorage.Type[] = []
Expand All @@ -26,7 +30,7 @@ class LocalImageSource extends ImageSource<LocalImageSource.Config> {
this.languages = config.languages
this.logger = ctx.logger('booru-local')

if (this.config.storage === 'database') this.ctx.using(['database'], async (ctx, options) => {
if (config.storage === 'database') ctx.using(['database'], async (ctx) => {
ctx.model.extend('booru_local', {
storeId: 'string',
storeName: 'text',
Expand All @@ -42,7 +46,7 @@ class LocalImageSource extends ImageSource<LocalImageSource.Config> {
this.imageMap = await ctx.database.get('booru_local', {})
})

if (this.config.storage === 'file') {
if (config.storage === 'file') {
const absMap = resolve(ctx.root.baseDir, LocalImageSource.DataDir, LocalImageSource.RootMap)
if (!existsSync(resolve(ctx.root.baseDir, LocalImageSource.DataDir)))
mkdirs(resolve(ctx.root.baseDir, LocalImageSource.DataDir))
Expand All @@ -61,7 +65,7 @@ class LocalImageSource extends ImageSource<LocalImageSource.Config> {
}

// TODO: cache storage
if (this.config.storage === 'cache') this.ctx.using(['cache'], () => { })
if (config.storage === 'cache') ctx.using(['cache'], () => { })

ctx.on('ready', async () => {
if (config.endpoint.length <= 0) return this.logger.warn('no folder yet')
Expand All @@ -72,6 +76,8 @@ class LocalImageSource extends ImageSource<LocalImageSource.Config> {
images: 0
}
this.logger.info('Initializing storages...')
// duplicate check
this.config.endpoint = [...new Set(this.config.endpoint)]
if (this.imageMap.length > 0) mapping = mapping.update(this.imageMap)
// mapping the folders to memory by loop
for await (let path of config.endpoint) {
Expand Down Expand Up @@ -102,11 +108,34 @@ class LocalImageSource extends ImageSource<LocalImageSource.Config> {

async get(query: ImageSource.Query): Promise<ImageSource.Result[]> {
if (this.imageMap.length < 1) return undefined
const map = this.imageMap.length === 1 ? this.imageMap[0] : Random.pick(this.imageMap)
if (query.tags.length > 0) {
map.images = map.images.filter(img => [...new Set([...img.tags, ...query.tags])].length > 0)
let pickPool = [];
// Flatten all maps
if (this.imageMap.length > 1) {
for (const storage of this.imageMap) {
if (query.tags.length > 0) {
// filter by tags
for (const image of storage.images) {
if (query.tags.every(tag => image.tags.includes(tag)))
pickPool.push(image)
}
} else {
// pick from all images
pickPool.push(...storage.images)
}
}
} else {
// pick from one image map
pickPool = this.imageMap.map((storage) => {
if (query.tags.length > 0) {
// filter by tags
return storage.images.filter((image) => query.tags.every(tag => image.tags.includes(tag)))
} else {
// pick from all images
return storage.images
}
}).flat()
}
const picker = Random.pick(map.images, query.count)
const picker = Random.pick(pickPool, query.count)
return picker.map(img => {
return {
url: pathToFileURL(img.path).href,
Expand Down Expand Up @@ -137,7 +166,7 @@ namespace LocalImageSource {
// TODO: Schema.path()?
endpoint: Schema.array(String).description('图源文件夹,支持多个不同的文件夹'),
storage: Schema.union<Mapping.Storage>(['file', 'database']).description('图源数据保存方式').default('file'),
reload: Schema.boolean().description('每次启动时重新加载所有图片').default(false),
reload: Schema.boolean().description('每次启动时重新构建图源数据').default(false),
languages: Schema.array(String).description('支持的语言').default(['zh-CN'])
}).description('图源设置'),
Schema.object({
Expand Down
6 changes: 3 additions & 3 deletions packages/local/src/scraper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ const element = {
tag: '(\\[.+\\])',
}

const nfsw = [true, false, 'furry', 'guro', 'shota', 'bl']
type Nfsw = boolean | 'furry' | 'guro' | 'shota' | 'bl'
const nsfw = [true, false, 'furry', 'guro', 'shota', 'bl']
type Nsfw = boolean | 'furry' | 'guro' | 'shota' | 'bl'

const format = {
filename: (name: string) => name,
tag: (tags: string) => tags.slice(1, -1).replace(',', ',').split(',').map(s => s.trim()),
nfsw: (tag: string) => nfsw.includes(tag.split('=')[1])
nsfw: (tag: string) => nsfw.includes(tag.split('=')[1])
}

const mapping = {
Expand Down