Skip to content
Open
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
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@ application-oauth2.yml
/benchmark
/release_notes

### Local Testing
test-config/
test-data/
Dockerfile.local
docker-compose.test.yml
TESTING_LOCALLY.md
QUICK_START.md
README_TESTING.md
WEBUI_BUILD_NOTE.md
WHAT_WENT_WRONG.md
FRONTEND_IMPLEMENTATION_NEEDED.md
start-test.ps1
stop-test.ps1
test-api.ps1
CHARACTERS_IMPLEMENTATION_SUMMARY.md

### Conveyor
output/
secret/
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"java.compile.nullAnalysis.mode": "disabled"
}
45 changes: 45 additions & 0 deletions komga-webui/src/components/dialogs/EditBooksDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,32 @@
</v-combobox>
</v-col>
</v-row>

<!-- Characters -->
<v-row>
<v-col cols="12">
<span class="text-body-2">{{ $t('common.characters') }}</span>
<v-combobox v-model="form.characters"
:items="charactersAvailable"
@input="$v.form.characters.$touch()"
@change="form.charactersLock = true"
hide-selected
chips
deletable-chips
multiple
filled
dense
>
<template v-slot:prepend>
<v-icon :color="form.charactersLock ? 'secondary' : ''"
@click="form.charactersLock = !form.charactersLock"
>
{{ form.charactersLock ? 'mdi-lock' : 'mdi-lock-open' }}
</v-icon>
</template>
</v-combobox>
</v-col>
</v-row>
</v-container>
</v-card>
</v-tab-item>
Expand Down Expand Up @@ -468,6 +494,8 @@ export default Vue.extend({
authorsLock: false,
tags: [] as string[],
tagsLock: false,
characters: [] as string[],
charactersLock: false,
isbn: '',
isbnLock: false,
links: [],
Expand All @@ -482,6 +510,7 @@ export default Vue.extend({
authorSearch: [],
authorSearchResults: [] as string[],
tagsAvailable: [] as string[],
charactersAvailable: [] as string[],
}
},
props: {
Expand All @@ -499,6 +528,7 @@ export default Vue.extend({
if (val) {
this.getThumbnails(this.books)
this.loadAvailableTags()
this.loadAvailableCharacters()
} else {
this.dialogCancel()
}
Expand Down Expand Up @@ -535,6 +565,7 @@ export default Vue.extend({
}),
},
tags: {},
characters: {},
releaseDate: {validDate},
summary: {},
authors: {},
Expand Down Expand Up @@ -591,6 +622,9 @@ export default Vue.extend({
async loadAvailableTags() {
this.tagsAvailable = await this.$komgaReferential.getTags()
},
async loadAvailableCharacters() {
this.charactersAvailable = await this.$komgaReferential.getCharacters()
},
linksLabelRules(label: string): boolean | string {
if (!!this.$_.trim(label)) return true
return this.$t('common.required').toString()
Expand Down Expand Up @@ -637,8 +671,14 @@ export default Vue.extend({

const tagsLock = this.$_.uniq(books.map(x => x.metadata.tagsLock))
this.form.tagsLock = tagsLock.length > 1 ? false : tagsLock[0]

this.form.characters = []

const charactersLock = this.$_.uniq(books.map(x => x.metadata.charactersLock))
this.form.charactersLock = charactersLock.length > 1 ? false : charactersLock[0]
} else {
this.form.tags = []
this.form.characters = []
this.form.links = []
const book = books as BookDto
this.$_.merge(this.form, book.metadata)
Expand All @@ -663,6 +703,7 @@ export default Vue.extend({
const metadata = {
authorsLock: this.form.authorsLock,
tagsLock: this.form.tagsLock,
charactersLock: this.form.charactersLock,
}

if (this.$v.form?.authors?.$dirty) {
Expand All @@ -677,6 +718,10 @@ export default Vue.extend({
this.$_.merge(metadata, {tags: this.form.tags})
}

if (this.$v.form?.characters?.$dirty) {
this.$_.merge(metadata, {characters: this.form.characters})
}

if (this.single) {
this.$_.merge(metadata, {
titleLock: this.form.titleLock,
Expand Down
40 changes: 40 additions & 0 deletions komga-webui/src/components/dialogs/EditOneshotDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,32 @@
</v-combobox>
</v-col>
</v-row>

<!-- Characters -->
<v-row>
<v-col cols="12">
<span class="text-body-2">{{ $t('dialog.edit_books.field_characters') }}</span>
<v-combobox v-model="form.book.characters"
:items="charactersAvailable"
@input="$v.form.book.characters.$touch()"
@change="form.book.charactersLock = true"
hide-selected
chips
deletable-chips
multiple
filled
dense
>
<template v-slot:prepend>
<v-icon :color="form.book.charactersLock ? 'secondary' : ''"
@click="form.book.charactersLock = !form.book.charactersLock"
>
{{ form.book.charactersLock ? 'mdi-lock' : 'mdi-lock-open' }}
</v-icon>
</template>
</v-combobox>
</v-col>
</v-row>
</v-container>
</v-card>
</v-tab-item>
Expand Down Expand Up @@ -621,6 +647,8 @@ export default Vue.extend({
authorsLock: false,
tags: [],
tagsLock: false,
characters: [],
charactersLock: false,
isbn: '',
isbnLock: false,
links: [],
Expand All @@ -643,6 +671,7 @@ export default Vue.extend({
authorSearchResults: [] as string[],
genresAvailable: [] as string[],
tagsAvailable: [] as string[],
charactersAvailable: [] as string[],
sharingLabelsAvailable: [] as string[],
}
},
Expand All @@ -661,6 +690,7 @@ export default Vue.extend({
if (val) {
this.getThumbnails(this.books)
this.loadAvailableTags()
this.loadAvailableCharacters()
this.loadAvailableGenres()
this.loadAvailableSharingLabels()
} else {
Expand Down Expand Up @@ -706,6 +736,7 @@ export default Vue.extend({
},
summary: {},
tags: {},
characters: {},
releaseDate: {validDate},
links: {},
authors: {},
Expand Down Expand Up @@ -807,6 +838,9 @@ export default Vue.extend({
async loadAvailableTags() {
this.tagsAvailable = await this.$komgaReferential.getTags()
},
async loadAvailableCharacters() {
this.charactersAvailable = await this.$komgaReferential.getCharacters()
},
async loadAvailableGenres() {
this.genresAvailable = await this.$komgaReferential.getGenres()
},
Expand Down Expand Up @@ -874,6 +908,11 @@ export default Vue.extend({
const tagsLock = this.$_.uniq(oneshots.map(x => x.book.metadata.tagsLock))
this.form.book.tagsLock = tagsLock.length > 1 ? false : tagsLock[0]

this.form.book.characters = []

const charactersLock = this.$_.uniq(oneshots.map(x => x.book.metadata.charactersLock))
this.form.book.charactersLock = charactersLock.length > 1 ? false : charactersLock[0]

this.form.series.sharingLabels = []

const sharingLabelsLock = this.$_.uniq(oneshots.map(x => x.series.metadata.sharingLabelsLock))
Expand All @@ -888,6 +927,7 @@ export default Vue.extend({
this.form.series.genres = []
this.form.series.sharingLabels = []
this.form.book.tags = []
this.form.book.characters = []
this.form.book.links = []
const oneshot = oneshots as Oneshot
this.$_.merge(this.form.series, oneshot.series.metadata)
Expand Down
39 changes: 39 additions & 0 deletions komga-webui/src/components/dialogs/EditSeriesDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,32 @@
</v-combobox>
</v-col>
</v-row>

<!-- Characters -->
<v-row>
<v-col cols="12">
<span class="text-body-2">{{ $t('dialog.edit_series.field_characters') }}</span>
<v-combobox v-model="form.characters"
:items="charactersAvailable"
@input="$v.form.characters.$touch()"
@change="form.charactersLock = true"
hide-selected
chips
deletable-chips
multiple
filled
dense
>
<template v-slot:prepend>
<v-icon :color="form.charactersLock ? 'secondary' : ''"
@click="form.charactersLock = !form.charactersLock"
>
{{ form.charactersLock ? 'mdi-lock' : 'mdi-lock-open' }}
</v-icon>
</template>
</v-combobox>
</v-col>
</v-row>
</v-container>
</v-card>
</v-tab-item>
Expand Down Expand Up @@ -597,6 +623,8 @@ export default Vue.extend({
genresLock: false,
tags: [],
tagsLock: false,
characters: [],
charactersLock: false,
totalBookCount: undefined as number | undefined,
totalBookCountLock: false,
sharingLabels: [],
Expand All @@ -621,6 +649,7 @@ export default Vue.extend({
},
genresAvailable: [] as string[],
tagsAvailable: [] as string[],
charactersAvailable: [] as string[],
sharingLabelsAvailable: [] as string[],
}
},
Expand All @@ -639,6 +668,7 @@ export default Vue.extend({
if(val) {
this.getThumbnails(this.series)
this.loadAvailableTags()
this.loadAvailableCharacters()
this.loadAvailableGenres()
this.loadAvailableSharingLabels()
} else {
Expand Down Expand Up @@ -744,6 +774,9 @@ export default Vue.extend({
async loadAvailableTags() {
this.tagsAvailable = await this.$komgaReferential.getTags()
},
async loadAvailableCharacters() {
this.charactersAvailable = await this.$komgaReferential.getCharacters()
},
async loadAvailableGenres() {
this.genresAvailable = await this.$komgaReferential.getGenres()
},
Expand Down Expand Up @@ -809,6 +842,11 @@ export default Vue.extend({
const tagsLock = this.$_.uniq(series.map(x => x.metadata.tagsLock))
this.form.tagsLock = tagsLock.length > 1 ? false : tagsLock[0]

this.form.characters = []

const charactersLock = this.$_.uniq(series.map(x => x.metadata.charactersLock))
this.form.charactersLock = charactersLock.length > 1 ? false : charactersLock[0]

this.form.sharingLabels = []

const sharingLabelsLock = this.$_.uniq(series.map(x => x.metadata.sharingLabelsLock))
Expand All @@ -819,6 +857,7 @@ export default Vue.extend({
} else {
this.form.genres = []
this.form.tags = []
this.form.characters = []
this.form.sharingLabels = []
this.form.links = []
this.form.alternateTitles = []
Expand Down
3 changes: 3 additions & 0 deletions komga-webui/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@
"settings": "Settings",
"sidecars": "Sidecars",
"tags": "Tags",
"characters": "Characters",
"ui": "User Interface",
"unavailable": "Unavailable",
"unlock_all": "Unlock all",
Expand Down Expand Up @@ -434,6 +435,7 @@
"field_release_date_error": "Must be a valid date in YYYY-MM-DD format",
"field_summary": "Summary",
"field_tags": "Tags",
"field_characters": "Characters",
"field_title": "Title",
"number_sort_decrement": "Decrement all by 1",
"number_sort_increment": "Increment all by 1",
Expand Down Expand Up @@ -838,6 +840,7 @@
"age_rating": "age rating",
"age_rating_none": "None",
"any": "Any",
"character": "character",
"complete": "Complete",
"genre": "genre",
"in_progress": "In Progress",
Expand Down
31 changes: 31 additions & 0 deletions komga-webui/src/services/komga-referential.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ export default class KomgaReferentialService {
}
}

async getCharacters(): Promise<string[]> {
try {
return (await this.http.get('/api/v1/characters')).data
} catch (e) {
let msg = 'An error occurred while trying to retrieve characters'
if (e.response.data.message) {
msg += `: ${e.response.data.message}`
}
throw new Error(msg)
}
}

async getSharingLabels(libraryIds?: string[], collectionId?: string): Promise<string[]> {
try {
const params = {} as any
Expand Down Expand Up @@ -123,6 +135,25 @@ export default class KomgaReferentialService {
}
}

async getSeriesAndBookCharacters(libraryIds?: string[], collectionId?: string): Promise<string[]> {
try {
const params = {} as any
if (libraryIds) params.library_id = libraryIds
if (collectionId) params.collection_id = collectionId

return (await this.http.get('/api/v1/characters', {
params: params,
paramsSerializer: params => qs.stringify(params, {indices: false}),
})).data
} catch (e) {
let msg = 'An error occurred while trying to retrieve series and book characters'
if (e.response.data.message) {
msg += `: ${e.response.data.message}`
}
throw new Error(msg)
}
}

async getBookTags(seriesId?: string, readListId?: string, libraryIds?: string[]): Promise<string[]> {
try {
const params = {} as any
Expand Down
Loading