forked from gotson/komga
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
related to gotson#30
- Loading branch information
Showing
19 changed files
with
1,268 additions
and
3 deletions.
There are no files selected for viewing
23 changes: 23 additions & 0 deletions
23
komga/src/flyway/resources/db/migration/V20200610172313__collections.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
create table collection | ||
( | ||
id bigint not null, | ||
name varchar not null, | ||
ordered boolean not null default false, | ||
series_count int not null, | ||
created_date timestamp not null default now(), | ||
last_modified_date timestamp not null default now(), | ||
primary key (id) | ||
); | ||
|
||
create table collection_series | ||
( | ||
collection_id bigint not null, | ||
series_id bigint not null, | ||
number integer not null | ||
); | ||
|
||
alter table collection_series | ||
add constraint fk_collection_series_collection_collection_id foreign key (collection_id) references collection (id); | ||
|
||
alter table collection_series | ||
add constraint fk_collection_series_series_series_id foreign key (series_id) references series (id); |
20 changes: 20 additions & 0 deletions
20
komga/src/main/kotlin/org/gotson/komga/domain/model/SeriesCollection.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package org.gotson.komga.domain.model | ||
|
||
import java.time.LocalDateTime | ||
|
||
data class SeriesCollection( | ||
val name: String, | ||
val ordered: Boolean = false, | ||
|
||
val seriesIds: List<Long> = emptyList(), | ||
|
||
val id: Long = 0, | ||
|
||
override val createdDate: LocalDateTime = LocalDateTime.now(), | ||
override val lastModifiedDate: LocalDateTime = LocalDateTime.now(), | ||
|
||
/** | ||
* Indicates that the seriesIds have been filtered and is not exhaustive. | ||
*/ | ||
val filtered: Boolean = false | ||
) : Auditable() |
36 changes: 36 additions & 0 deletions
36
komga/src/main/kotlin/org/gotson/komga/domain/persistence/SeriesCollectionRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package org.gotson.komga.domain.persistence | ||
|
||
import org.gotson.komga.domain.model.SeriesCollection | ||
|
||
interface SeriesCollectionRepository { | ||
fun findByIdOrNull(collectionId: Long): SeriesCollection? | ||
fun findAll(): Collection<SeriesCollection> | ||
|
||
/** | ||
* Find one SeriesCollection by collectionId, | ||
* optionally with only seriesId filtered by the provided filterOnLibraryIds. | ||
*/ | ||
fun findByIdOrNull(collectionId: Long, filterOnLibraryIds: Collection<Long>?): SeriesCollection? | ||
|
||
/** | ||
* Find all SeriesCollection with at least one Series belonging to the provided belongsToLibraryIds, | ||
* optionally with only seriesId filtered by the provided filterOnLibraryIds. | ||
*/ | ||
fun findAllByLibraries(belongsToLibraryIds: Collection<Long>, filterOnLibraryIds: Collection<Long>?): Collection<SeriesCollection> | ||
|
||
/** | ||
* Find all SeriesCollection that contains the provided containsSeriesId, | ||
* optionally with only seriesId filtered by the provided filterOnLibraryIds. | ||
*/ | ||
fun findAllBySeries(containsSeriesId: Long, filterOnLibraryIds: Collection<Long>?): Collection<SeriesCollection> | ||
|
||
fun insert(collection: SeriesCollection): SeriesCollection | ||
fun update(collection: SeriesCollection) | ||
|
||
fun removeSeriesFromAll(seriesId: Long) | ||
|
||
fun delete(collectionId: Long) | ||
fun deleteAll() | ||
|
||
fun existsByName(name: String): Boolean | ||
} |
41 changes: 41 additions & 0 deletions
41
komga/src/main/kotlin/org/gotson/komga/domain/service/SeriesCollectionLifecycle.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package org.gotson.komga.domain.service | ||
|
||
import mu.KotlinLogging | ||
import org.gotson.komga.domain.model.DuplicateNameException | ||
import org.gotson.komga.domain.model.SeriesCollection | ||
import org.gotson.komga.domain.persistence.SeriesCollectionRepository | ||
import org.springframework.stereotype.Service | ||
|
||
private val logger = KotlinLogging.logger {} | ||
|
||
@Service | ||
class SeriesCollectionLifecycle( | ||
private val collectionRepository: SeriesCollectionRepository | ||
) { | ||
|
||
@Throws( | ||
DuplicateNameException::class | ||
) | ||
fun addCollection(collection: SeriesCollection): SeriesCollection { | ||
logger.info { "Adding new collection: $collection" } | ||
|
||
if (collectionRepository.existsByName(collection.name)) | ||
throw DuplicateNameException("Collection name already exists") | ||
|
||
return collectionRepository.insert(collection) | ||
} | ||
|
||
fun updateCollection(toUpdate: SeriesCollection) { | ||
val existing = collectionRepository.findByIdOrNull(toUpdate.id) | ||
?: throw IllegalArgumentException("Cannot update collection that does not exist") | ||
|
||
if (existing.name != toUpdate.name && collectionRepository.existsByName(toUpdate.name)) | ||
throw DuplicateNameException("Collection name already exists") | ||
|
||
collectionRepository.update(toUpdate) | ||
} | ||
|
||
fun deleteCollection(collectionId: Long) { | ||
collectionRepository.delete(collectionId) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
komga/src/main/kotlin/org/gotson/komga/infrastructure/image/MosaicGenerator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package org.gotson.komga.infrastructure.image | ||
|
||
import net.coobird.thumbnailator.Thumbnails | ||
import org.springframework.stereotype.Service | ||
import java.awt.image.BufferedImage | ||
import java.io.ByteArrayOutputStream | ||
import javax.imageio.ImageIO | ||
|
||
|
||
@Service | ||
class MosaicGenerator { | ||
|
||
fun createMosaic(images: List<ByteArray>): ByteArray { | ||
val thumbs = images.map { resize(it, 150) } | ||
|
||
return ByteArrayOutputStream().use { baos -> | ||
val mosaic = BufferedImage(212, 300, BufferedImage.TYPE_INT_RGB) | ||
mosaic.createGraphics().apply { | ||
listOf( | ||
0 to 0, | ||
106 to 0, | ||
0 to 150, | ||
106 to 150 | ||
).forEachIndexed { index, (x, y) -> | ||
thumbs.getOrNull(index)?.let { drawImage(it, x, y, null) } | ||
} | ||
} | ||
|
||
ImageIO.write(mosaic, "jpeg", baos) | ||
|
||
baos.toByteArray() | ||
} | ||
} | ||
|
||
private fun resize(imageBytes: ByteArray, size: Int) = | ||
Thumbnails.of(imageBytes.inputStream()) | ||
.size(size, size) | ||
.outputFormat("jpeg") | ||
.asBufferedImage() | ||
} |
188 changes: 188 additions & 0 deletions
188
komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesCollectionDao.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
package org.gotson.komga.infrastructure.jooq | ||
|
||
import org.gotson.komga.domain.model.SeriesCollection | ||
import org.gotson.komga.domain.persistence.SeriesCollectionRepository | ||
import org.gotson.komga.jooq.Sequences | ||
import org.gotson.komga.jooq.Tables | ||
import org.gotson.komga.jooq.tables.records.CollectionRecord | ||
import org.jooq.DSLContext | ||
import org.jooq.Record | ||
import org.jooq.ResultQuery | ||
import org.springframework.stereotype.Component | ||
import java.time.LocalDateTime | ||
|
||
@Component | ||
class SeriesCollectionDao( | ||
private val dsl: DSLContext | ||
) : SeriesCollectionRepository { | ||
|
||
private val c = Tables.COLLECTION | ||
private val cs = Tables.COLLECTION_SERIES | ||
private val s = Tables.SERIES | ||
|
||
private val groupFields = arrayOf(*c.fields(), *cs.fields()) | ||
|
||
|
||
override fun findByIdOrNull(collectionId: Long): SeriesCollection? = | ||
selectBase() | ||
.where(c.ID.eq(collectionId)) | ||
.groupBy(*groupFields) | ||
.orderBy(cs.NUMBER.asc()) | ||
.fetchAndMap() | ||
.firstOrNull() | ||
|
||
override fun findByIdOrNull(collectionId: Long, filterOnLibraryIds: Collection<Long>?): SeriesCollection? = | ||
selectBase() | ||
.where(c.ID.eq(collectionId)) | ||
.also { step -> | ||
filterOnLibraryIds?.let { step.and(s.LIBRARY_ID.`in`(it)) } | ||
} | ||
.groupBy(*groupFields) | ||
.orderBy(cs.NUMBER.asc()) | ||
.fetchAndMap() | ||
.firstOrNull() | ||
|
||
override fun findAll(): Collection<SeriesCollection> = | ||
selectBase() | ||
.groupBy(*groupFields) | ||
.orderBy(cs.NUMBER.asc()) | ||
.fetchAndMap() | ||
|
||
override fun findAllByLibraries(belongsToLibraryIds: Collection<Long>, filterOnLibraryIds: Collection<Long>?): Collection<SeriesCollection> { | ||
val ids = dsl.select(c.ID) | ||
.from(c) | ||
.leftJoin(cs).on(c.ID.eq(cs.COLLECTION_ID)) | ||
.leftJoin(s).on(cs.SERIES_ID.eq(s.ID)) | ||
.where(s.LIBRARY_ID.`in`(belongsToLibraryIds)) | ||
.fetch(0, Long::class.java) | ||
|
||
return selectBase() | ||
.where(c.ID.`in`(ids)) | ||
.also { step -> | ||
filterOnLibraryIds?.let { step.and(s.LIBRARY_ID.`in`(it)) } | ||
} | ||
.groupBy(*groupFields) | ||
.orderBy(cs.NUMBER.asc()) | ||
.fetchAndMap() | ||
} | ||
|
||
override fun findAllBySeries(containsSeriesId: Long, filterOnLibraryIds: Collection<Long>?): Collection<SeriesCollection> { | ||
val ids = dsl.select(c.ID) | ||
.from(c) | ||
.leftJoin(cs).on(c.ID.eq(cs.COLLECTION_ID)) | ||
.where(cs.SERIES_ID.eq(containsSeriesId)) | ||
.fetch(0, Long::class.java) | ||
|
||
return selectBase() | ||
.where(c.ID.`in`(ids)) | ||
.also { step -> | ||
filterOnLibraryIds?.let { step.and(s.LIBRARY_ID.`in`(it)) } | ||
} | ||
.groupBy(*groupFields) | ||
.orderBy(cs.NUMBER.asc()) | ||
.fetchAndMap() | ||
} | ||
|
||
private fun selectBase() = | ||
dsl.select(*groupFields) | ||
.from(c) | ||
.leftJoin(cs).on(c.ID.eq(cs.COLLECTION_ID)) | ||
.leftJoin(s).on(cs.SERIES_ID.eq(s.ID)) | ||
|
||
private fun ResultQuery<Record>.fetchAndMap() = | ||
fetchGroups({ it.into(c) }, { it.into(cs) }) | ||
.map { (cr, csr) -> | ||
val seriesIds = csr.map { it.seriesId } | ||
cr.toDomain(seriesIds) | ||
} | ||
|
||
override fun insert(collection: SeriesCollection): SeriesCollection { | ||
val id = dsl.nextval(Sequences.HIBERNATE_SEQUENCE) | ||
val insert = collection.copy(id = id) | ||
|
||
dsl.insertInto(c) | ||
.set(c.ID, insert.id) | ||
.set(c.NAME, insert.name) | ||
.set(c.ORDERED, insert.ordered) | ||
.set(c.SERIES_COUNT, collection.seriesIds.size) | ||
.execute() | ||
|
||
insertSeries(insert) | ||
|
||
return findByIdOrNull(id)!! | ||
} | ||
|
||
|
||
private fun insertSeries(collection: SeriesCollection) { | ||
collection.seriesIds.forEachIndexed { index, id -> | ||
dsl.insertInto(cs) | ||
.set(cs.COLLECTION_ID, collection.id) | ||
.set(cs.SERIES_ID, id) | ||
.set(cs.NUMBER, index) | ||
.execute() | ||
} | ||
} | ||
|
||
override fun update(collection: SeriesCollection) { | ||
dsl.transaction { config -> | ||
with(config.dsl()) | ||
{ | ||
update(c) | ||
.set(c.NAME, collection.name) | ||
.set(c.ORDERED, collection.ordered) | ||
.set(c.SERIES_COUNT, collection.seriesIds.size) | ||
.set(c.LAST_MODIFIED_DATE, LocalDateTime.now()) | ||
.where(c.ID.eq(collection.id)) | ||
.execute() | ||
|
||
deleteFrom(cs).where(cs.COLLECTION_ID.eq(collection.id)).execute() | ||
|
||
insertSeries(collection) | ||
} | ||
} | ||
} | ||
|
||
override fun removeSeriesFromAll(seriesId: Long) { | ||
dsl.deleteFrom(cs) | ||
.where(cs.SERIES_ID.eq(seriesId)) | ||
.execute() | ||
} | ||
|
||
override fun delete(collectionId: Long) { | ||
dsl.transaction { config -> | ||
with(config.dsl()) | ||
{ | ||
deleteFrom(cs).where(cs.COLLECTION_ID.eq(collectionId)).execute() | ||
deleteFrom(c).where(c.ID.eq(collectionId)).execute() | ||
} | ||
} | ||
} | ||
|
||
override fun deleteAll() { | ||
dsl.transaction { config -> | ||
with(config.dsl()) | ||
{ | ||
deleteFrom(cs).execute() | ||
deleteFrom(c).execute() | ||
} | ||
} | ||
} | ||
|
||
override fun existsByName(name: String): Boolean = | ||
dsl.fetchExists( | ||
dsl.selectFrom(c) | ||
.where(c.NAME.equalIgnoreCase(name)) | ||
) | ||
|
||
|
||
private fun CollectionRecord.toDomain(seriesIds: List<Long>) = | ||
SeriesCollection( | ||
name = name, | ||
ordered = ordered, | ||
seriesIds = seriesIds, | ||
id = id, | ||
createdDate = createdDate, | ||
lastModifiedDate = lastModifiedDate, | ||
filtered = seriesCount != seriesIds.size | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.