feat: anilist metadata fetch
This commit is contained in:
+2
-2
@@ -10,8 +10,8 @@ identifier_name:
|
||||
min_length: 1
|
||||
allowed_symbols: ["_"]
|
||||
file_length:
|
||||
error: 10000
|
||||
warning: 3000
|
||||
error: 100000
|
||||
warning: 10000
|
||||
cyclomatic_complexity:
|
||||
warning: 50
|
||||
error: 100
|
||||
|
||||
@@ -45,11 +45,11 @@ struct GlobalState: Codable {
|
||||
|
||||
/// nil indicates a default should be used, aka what is in settings.
|
||||
struct LocalState: Codable {
|
||||
var progress: ProgressIndices = ProgressIndices()
|
||||
var progress: ProgressIndices = .init()
|
||||
var mode: PageTurnMode?
|
||||
var backgroundColor: String?
|
||||
var aniListTracking: Bool = false
|
||||
var scrollPos: CGPoint = CGPoint()
|
||||
var scrollPos: CGPoint = .init()
|
||||
}
|
||||
|
||||
struct Settings: Codable {
|
||||
@@ -1984,13 +1984,17 @@ class ComicMetadataViewController: UITableViewController {
|
||||
switch indexPath.row {
|
||||
case 0: cell.textLabel?.text = "Title"; cell.detailTextLabel?.text = comic.title
|
||||
case 1: cell.textLabel?.text = "AniList ID"; cell.detailTextLabel?.text = anId
|
||||
case 2: cell.textLabel?.text = "Format"; cell.detailTextLabel?.text = "\(comic.format)"
|
||||
case 2: cell.textLabel?.text = "Format"; cell.detailTextLabel?.text = comic.format != nil
|
||||
? "\(comic.format!)" : ""
|
||||
case 3: cell.textLabel?.text = "Original Language"; cell.detailTextLabel?.text = comic.originalLanguage
|
||||
case 4: cell.textLabel?.text = "Publication Demographic"
|
||||
cell.detailTextLabel?.text = "\(comic.publicationDemographic)"
|
||||
cell.detailTextLabel?.text = comic.publicationDemographic != nil
|
||||
? "\(comic.publicationDemographic!)" : ""
|
||||
case 5: cell.textLabel?.text = "Country of Origin"; cell.detailTextLabel?.text = comic.countryOfOrigin
|
||||
case 6: cell.textLabel?.text = "Status"; cell.detailTextLabel?.text = "\(comic.status)"
|
||||
case 7: cell.textLabel?.text = "Content Rating"; cell.detailTextLabel?.text = "\(comic.contentRating)"
|
||||
case 6: cell.textLabel?.text = "Status"; cell.detailTextLabel?.text = comic.status != nil
|
||||
? "\(comic.status!)" : ""
|
||||
case 7: cell.textLabel?.text = "Content Rating"; cell.detailTextLabel?.text = comic.contentRating != nil
|
||||
? "\(comic.contentRating!)" : ""
|
||||
case 8: cell.textLabel?.text = "First Release"
|
||||
cell.detailTextLabel?.text = "\(dateToString(comic.startReleaseDate))"
|
||||
case 9: cell.textLabel?.text = "Last Release"
|
||||
@@ -2365,7 +2369,7 @@ extension ViewController {
|
||||
children: aniListOptionItems,
|
||||
destructive: false,
|
||||
available: { [self] in
|
||||
return comics[indexPath.item].metadata.anilistId != nil && aniListToken != nil || true
|
||||
return comics[indexPath.item].metadata.anilistId != nil && aniListToken != nil
|
||||
},
|
||||
action: { [weak self] in
|
||||
self?.dismiss(animated: false)
|
||||
@@ -2420,6 +2424,16 @@ extension ViewController {
|
||||
self.state[self.currentPath.lastPathComponent]!.aniListTracking = v
|
||||
self.saveLocalState()
|
||||
}
|
||||
if action == .fetchMetadata {
|
||||
if let id = comics[indexPath.item].metadata.anilistId {
|
||||
AniListClient.fetchMetadata(id: UInt(id)) { result in
|
||||
switch result {
|
||||
case let .success(m): self.rebuildMetadata(m, indexPath: indexPath)
|
||||
case .failure: break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.dismiss(animated: false)
|
||||
}
|
||||
)
|
||||
@@ -2468,6 +2482,123 @@ extension ViewController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func rebuildMetadata(_ m: MediaMetadata, indexPath: IndexPath) {
|
||||
let alert = UIAlertController(
|
||||
title: "Failed to rebuild metadata",
|
||||
message: "This is an alert.",
|
||||
preferredStyle: .alert
|
||||
)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
|
||||
|
||||
let endDate: Date!
|
||||
if let d = m.endDate, let year = d.year, let month = d.month, let day = d.day {
|
||||
endDate = Date(year: UInt16(year), month: UInt8(month), day: UInt8(day))
|
||||
} else { endDate = Date(year: 0, month: 0, day: 0) }
|
||||
let startDate: Date!
|
||||
if let d = m.startDate, let year = d.year, let month = d.month, let day = d.day {
|
||||
startDate = Date(year: UInt16(year), month: UInt8(month), day: UInt8(day))
|
||||
} else { startDate = Date(year: 0, month: 0, day: 0) }
|
||||
|
||||
let nSource = Source(from: m.source)
|
||||
let nFormat = Format(from: m.format)
|
||||
let nStatus = Status(from: m.status)
|
||||
let pM = comics[indexPath.item].metadata
|
||||
var volumes: [Offset] = []
|
||||
var currentChapters: [Offset] = []
|
||||
var currentImages: [Offset] = []
|
||||
var builder = FlatBufferBuilder(initialSize: 1024)
|
||||
for volume in pM.volumes {
|
||||
for chapter in volume.chapters {
|
||||
for image in chapter.images {
|
||||
let m_filename = builder.create(string: image.filename)
|
||||
let imageMetadata = ImageMetadata.createImageMetadata(
|
||||
&builder,
|
||||
doublePage: image.doublePage,
|
||||
filenameOffset: m_filename,
|
||||
firstPage: image.firstPage,
|
||||
size: image.size
|
||||
)
|
||||
currentImages.append(imageMetadata)
|
||||
}
|
||||
let chapterName = builder.create(string: chapter.name)
|
||||
let imagesOffset = builder.createVector(ofOffsets: currentImages)
|
||||
let chapterMetadata = ChapterMetadata.createChapterMetadata(
|
||||
&builder,
|
||||
chapter: chapter.chapter,
|
||||
nameOffset: chapterName,
|
||||
imagesVectorOffset: imagesOffset
|
||||
)
|
||||
currentChapters.append(chapterMetadata)
|
||||
currentImages = []
|
||||
}
|
||||
let volumeTitle = builder.create(string: volume.title)
|
||||
let volumeURLOffset = builder.create(string: volume.archive)
|
||||
let chaptersOffset = builder.createVector(ofOffsets: currentChapters)
|
||||
let volumeMetadata = VolumeMetadata.createVolumeMetadata(
|
||||
&builder,
|
||||
volume: volume.volume,
|
||||
titleOffset: volumeTitle,
|
||||
archiveOffset: volumeURLOffset,
|
||||
chaptersVectorOffset: chaptersOffset
|
||||
)
|
||||
|
||||
volumes.append(volumeMetadata)
|
||||
currentChapters = []
|
||||
}
|
||||
|
||||
let title = builder.create(string: m.title?.english)
|
||||
let originalLanguage = builder.create(string: pM.originalLanguage)
|
||||
let countryOfOrigin = builder.create(string: pM.countryOfOrigin)
|
||||
let description = builder.create(string: m.description)
|
||||
let tags = builder.createVector(ofOffsets: [])
|
||||
let volumeOffsets = builder.createVector(ofOffsets: volumes)
|
||||
let metadata = Metadata.createMetadata(
|
||||
&builder,
|
||||
titleOffset: title,
|
||||
format: nFormat,
|
||||
originalLanguageOffset: originalLanguage,
|
||||
countryOfOriginOffset: countryOfOrigin,
|
||||
publicationDemographic: pM.publicationDemographic,
|
||||
status: nStatus,
|
||||
contentRating: pM.contentRating,
|
||||
source: nSource,
|
||||
startReleaseDate: startDate ?? Date(year: 0, month: 0, day: 0),
|
||||
endReleaseDate: endDate ?? Date(year: 0, month: 0, day: 0),
|
||||
tagsVectorOffset: tags,
|
||||
descriptionOffset: description,
|
||||
anilistId: pM.anilistId,
|
||||
volumesVectorOffset: volumeOffsets,
|
||||
imageCount: pM.imageCount,
|
||||
)
|
||||
|
||||
builder.finish(offset: metadata)
|
||||
do {
|
||||
try builder.data.write(to: currentPath.appendingPathComponent(metadataFilename))
|
||||
} catch {
|
||||
alert.message = String(format: "Failed to write metadata file at: \(metadataFilename)")
|
||||
DispatchQueue.main.async {
|
||||
self.present(alert, animated: false)
|
||||
}
|
||||
}
|
||||
let metadataPath = currentPath.appendingPathComponent(metadataFilename)
|
||||
guard let data = try? Data(contentsOf: metadataPath) else {
|
||||
alert.message = String(format: "Failed to read metadata file at: \(metadataPath)")
|
||||
DispatchQueue.main.async {
|
||||
self.present(alert, animated: false)
|
||||
}
|
||||
return
|
||||
}
|
||||
var byteBuffer = ByteBuffer(data: data)
|
||||
guard let newMetadata: Metadata = try? getCheckedRoot(byteBuffer: &byteBuffer) else {
|
||||
alert.message = String(format: "Failed to load flatbuffer file at: \(metadataPath)")
|
||||
DispatchQueue.main.async {
|
||||
self.present(alert, animated: false)
|
||||
}
|
||||
return
|
||||
}
|
||||
comics[indexPath.item].metadata = newMetadata
|
||||
}
|
||||
}
|
||||
|
||||
extension ViewController: UIPopoverPresentationControllerDelegate {
|
||||
@@ -2545,6 +2676,32 @@ struct AniListResponse<T: Decodable>: Decodable {
|
||||
let data: T
|
||||
}
|
||||
|
||||
struct MediaMetadataData: Decodable {
|
||||
// swiftlint:disable identifier_name
|
||||
let Media: MediaMetadata
|
||||
// swiftlint:enable identifier_name
|
||||
}
|
||||
|
||||
struct MediaDate: Decodable {
|
||||
let year: Int?
|
||||
let month: Int?
|
||||
let day: Int?
|
||||
}
|
||||
|
||||
struct MediaMetadata: Decodable {
|
||||
let description: String?
|
||||
let endDate: MediaDate?
|
||||
let startDate: MediaDate?
|
||||
let format: String?
|
||||
let title: MediaTitle?
|
||||
let source: String?
|
||||
let status: String?
|
||||
}
|
||||
|
||||
struct MediaTitle: Decodable {
|
||||
let english: String?
|
||||
}
|
||||
|
||||
struct ViewerData: Decodable {
|
||||
// swiftlint:disable identifier_name
|
||||
let Viewer: Viewer
|
||||
@@ -2670,6 +2827,96 @@ enum AniListClient {
|
||||
}
|
||||
}
|
||||
|
||||
static func fetchMetadata(
|
||||
id: UInt,
|
||||
completion: @escaping (Result<MediaMetadata, Error>) -> Void
|
||||
) {
|
||||
let query = """
|
||||
query($mediaId: Int = \(id)) {
|
||||
Media(id: $mediaId) {
|
||||
description
|
||||
endDate {
|
||||
day
|
||||
month
|
||||
year
|
||||
}
|
||||
format
|
||||
startDate {
|
||||
day
|
||||
month
|
||||
year
|
||||
}
|
||||
title {
|
||||
english
|
||||
}
|
||||
source
|
||||
status
|
||||
}
|
||||
}
|
||||
"""
|
||||
fetch(
|
||||
query: query
|
||||
) { (result: Result<AniListResponse<MediaMetadataData>, Error>) in
|
||||
switch result {
|
||||
case let .success(m):
|
||||
let metadata = m.data.Media
|
||||
completion(.success(metadata))
|
||||
case let .failure(error):
|
||||
completion(.failure(error))
|
||||
print("Error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func fetch<T: Decodable>(
|
||||
query: String,
|
||||
completion: @escaping (Result<AniListResponse<T>, Error>) -> Void
|
||||
) {
|
||||
var request = URLRequest(url: endpoint)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
request.setValue("application/json", forHTTPHeaderField: "Accept")
|
||||
|
||||
do {
|
||||
request.httpBody = try JSONEncoder().encode(GraphQLBody(query: query))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
return
|
||||
}
|
||||
|
||||
let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
||||
if let error = error {
|
||||
completion(.failure(error))
|
||||
return
|
||||
}
|
||||
|
||||
guard let http = response as? HTTPURLResponse else {
|
||||
completion(.failure(AniListError.badResponse))
|
||||
return
|
||||
}
|
||||
|
||||
guard let data = data else {
|
||||
completion(.failure(AniListError.noData))
|
||||
return
|
||||
}
|
||||
|
||||
guard (200 ... 299).contains(http.statusCode) else {
|
||||
let body = String(data: data, encoding: .utf8) ?? "<no body>"
|
||||
completion(.failure(AniListError.requestFailed(status: http.statusCode, body: body)))
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let decoded = try JSONDecoder().decode(AniListResponse<T>.self, from: data)
|
||||
completion(.success(decoded))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
task.resume()
|
||||
}
|
||||
|
||||
static func fetchMediaStatus(
|
||||
accessToken: String,
|
||||
id: UInt,
|
||||
@@ -2824,3 +3071,65 @@ enum AniListClient {
|
||||
task.resume()
|
||||
}
|
||||
}
|
||||
|
||||
extension Source {
|
||||
init?(from string: String?) {
|
||||
guard let string = string else { return nil }
|
||||
guard let source = Source(from: string) else { return nil }
|
||||
self = source
|
||||
}
|
||||
init?(from string: String) {
|
||||
switch string.lowercased() {
|
||||
case "ORIGINAL": self = .original
|
||||
case "MANGA": self = .manga
|
||||
case "LIGHT_NOVEL": self = .lightnovel
|
||||
case "VISUAL_NOVEL": self = .visualnovel
|
||||
case "VIDEO_GAME": self = .videogame
|
||||
case "OTHER": self = .other
|
||||
case "NOVEL": self = .novel
|
||||
case "DOUJINSHI": self = .doujinshi
|
||||
case "ANIME": self = .anime
|
||||
case "WEB_NOVEL": self = .webnovel
|
||||
case "LIVE_ACTION": self = .liveaction
|
||||
case "GAME": self = .game
|
||||
case "COMIC": self = .comic
|
||||
case "MULTIMEDIA_PROJECT": self = .multimediaproject
|
||||
case "PICTUREBOOK": self = .picturebook
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Format {
|
||||
init?(from string: String?) {
|
||||
guard let string = string else { return nil }
|
||||
guard let format = Format(from: string) else { return nil }
|
||||
self = format
|
||||
}
|
||||
init?(from string: String) {
|
||||
switch string {
|
||||
case "MANGA": self = .manga
|
||||
case "NOVEL": self = .novel
|
||||
case "ONE_SHOT": self = .oneshot
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Status {
|
||||
init?(from string: String?) {
|
||||
guard let string = string else { return nil }
|
||||
guard let status = Status(from: string) else { return nil }
|
||||
self = status
|
||||
}
|
||||
init?(from string: String) {
|
||||
switch string {
|
||||
case "FINISHED": self = .finished
|
||||
case "RELEASING": self = .releasing
|
||||
case "HIATUS": self = .hiatus
|
||||
case "NOT_YET_RELEASED": self = .notyetreleased
|
||||
case "CANCELLED": self = .cancelled
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,13 +49,13 @@ table VolumeMetadata {
|
||||
|
||||
table Metadata {
|
||||
title:string (required);
|
||||
format:Format;
|
||||
format:Format = null;
|
||||
original_language:string;
|
||||
country_of_origin:string;
|
||||
publication_demographic:PublicationDemographic;
|
||||
status:Status;
|
||||
content_rating:ContentRating;
|
||||
source:Source;
|
||||
publication_demographic:PublicationDemographic = null;
|
||||
status:Status = null;
|
||||
content_rating:ContentRating = null;
|
||||
source:Source = null;
|
||||
start_release_date:Date;
|
||||
end_release_date:Date;
|
||||
tags:[Tag];
|
||||
|
||||
@@ -455,15 +455,15 @@ public struct Metadata: FlatBufferTable, FlatbuffersVectorInitializable, Verifia
|
||||
|
||||
public var title: String! { let o = _accessor.offset(VT.title); return _accessor.string(at: o) }
|
||||
public var titleSegmentArray: [UInt8]! { return _accessor.getVector(at: VT.title) }
|
||||
public var format: Format { let o = _accessor.offset(VT.format); return o == 0 ? .manga : Format(rawValue: _accessor.readBuffer(of: Int8.self, at: o)) ?? .manga }
|
||||
public var format: Format? { let o = _accessor.offset(VT.format); return o == 0 ? nil : Format(rawValue: _accessor.readBuffer(of: Int8.self, at: o)) ?? nil }
|
||||
public var originalLanguage: String? { let o = _accessor.offset(VT.originalLanguage); return o == 0 ? nil : _accessor.string(at: o) }
|
||||
public var originalLanguageSegmentArray: [UInt8]? { return _accessor.getVector(at: VT.originalLanguage) }
|
||||
public var countryOfOrigin: String? { let o = _accessor.offset(VT.countryOfOrigin); return o == 0 ? nil : _accessor.string(at: o) }
|
||||
public var countryOfOriginSegmentArray: [UInt8]? { return _accessor.getVector(at: VT.countryOfOrigin) }
|
||||
public var publicationDemographic: PublicationDemographic { let o = _accessor.offset(VT.publicationDemographic); return o == 0 ? .shounen : PublicationDemographic(rawValue: _accessor.readBuffer(of: Int8.self, at: o)) ?? .shounen }
|
||||
public var status: Status { let o = _accessor.offset(VT.status); return o == 0 ? .finished : Status(rawValue: _accessor.readBuffer(of: Int8.self, at: o)) ?? .finished }
|
||||
public var contentRating: ContentRating { let o = _accessor.offset(VT.contentRating); return o == 0 ? .safe : ContentRating(rawValue: _accessor.readBuffer(of: Int8.self, at: o)) ?? .safe }
|
||||
public var source: Source { let o = _accessor.offset(VT.source); return o == 0 ? .original : Source(rawValue: _accessor.readBuffer(of: Int8.self, at: o)) ?? .original }
|
||||
public var publicationDemographic: PublicationDemographic? { let o = _accessor.offset(VT.publicationDemographic); return o == 0 ? nil : PublicationDemographic(rawValue: _accessor.readBuffer(of: Int8.self, at: o)) ?? nil }
|
||||
public var status: Status? { let o = _accessor.offset(VT.status); return o == 0 ? nil : Status(rawValue: _accessor.readBuffer(of: Int8.self, at: o)) ?? nil }
|
||||
public var contentRating: ContentRating? { let o = _accessor.offset(VT.contentRating); return o == 0 ? nil : ContentRating(rawValue: _accessor.readBuffer(of: Int8.self, at: o)) ?? nil }
|
||||
public var source: Source? { let o = _accessor.offset(VT.source); return o == 0 ? nil : Source(rawValue: _accessor.readBuffer(of: Int8.self, at: o)) ?? nil }
|
||||
public var startReleaseDate: Date? { let o = _accessor.offset(VT.startReleaseDate); return o == 0 ? nil : _accessor.readBuffer(of: Date.self, at: o) }
|
||||
public var mutableStartReleaseDate: Date_Mutable? { let o = _accessor.offset(VT.startReleaseDate); return o == 0 ? nil : Date_Mutable(_accessor.bb, o: o + _accessor.position) }
|
||||
public var endReleaseDate: Date? { let o = _accessor.offset(VT.endReleaseDate); return o == 0 ? nil : _accessor.readBuffer(of: Date.self, at: o) }
|
||||
@@ -476,13 +476,13 @@ public struct Metadata: FlatBufferTable, FlatbuffersVectorInitializable, Verifia
|
||||
public var imageCount: UInt32 { let o = _accessor.offset(VT.imageCount); return o == 0 ? 0 : _accessor.readBuffer(of: UInt32.self, at: o) }
|
||||
public static func startMetadata(_ fbb: inout FlatBufferBuilder) -> UOffset { fbb.startTable(with: 15) }
|
||||
public static func add(title: Offset, _ fbb: inout FlatBufferBuilder) { fbb.add(offset: title, at: VT.title) }
|
||||
public static func add(format: Format, _ fbb: inout FlatBufferBuilder) { fbb.add(element: format.rawValue, def: 0, at: VT.format) }
|
||||
public static func add(format: Format?, _ fbb: inout FlatBufferBuilder) { fbb.add(element: format?.rawValue, at: VT.format) }
|
||||
public static func add(originalLanguage: Offset, _ fbb: inout FlatBufferBuilder) { fbb.add(offset: originalLanguage, at: VT.originalLanguage) }
|
||||
public static func add(countryOfOrigin: Offset, _ fbb: inout FlatBufferBuilder) { fbb.add(offset: countryOfOrigin, at: VT.countryOfOrigin) }
|
||||
public static func add(publicationDemographic: PublicationDemographic, _ fbb: inout FlatBufferBuilder) { fbb.add(element: publicationDemographic.rawValue, def: 0, at: VT.publicationDemographic) }
|
||||
public static func add(status: Status, _ fbb: inout FlatBufferBuilder) { fbb.add(element: status.rawValue, def: 0, at: VT.status) }
|
||||
public static func add(contentRating: ContentRating, _ fbb: inout FlatBufferBuilder) { fbb.add(element: contentRating.rawValue, def: 0, at: VT.contentRating) }
|
||||
public static func add(source: Source, _ fbb: inout FlatBufferBuilder) { fbb.add(element: source.rawValue, def: 0, at: VT.source) }
|
||||
public static func add(publicationDemographic: PublicationDemographic?, _ fbb: inout FlatBufferBuilder) { fbb.add(element: publicationDemographic?.rawValue, at: VT.publicationDemographic) }
|
||||
public static func add(status: Status?, _ fbb: inout FlatBufferBuilder) { fbb.add(element: status?.rawValue, at: VT.status) }
|
||||
public static func add(contentRating: ContentRating?, _ fbb: inout FlatBufferBuilder) { fbb.add(element: contentRating?.rawValue, at: VT.contentRating) }
|
||||
public static func add(source: Source?, _ fbb: inout FlatBufferBuilder) { fbb.add(element: source?.rawValue, at: VT.source) }
|
||||
public static func add(startReleaseDate: Date?, _ fbb: inout FlatBufferBuilder) { guard let startReleaseDate = startReleaseDate else { return }; fbb.create(struct: startReleaseDate, position: VT.startReleaseDate) }
|
||||
public static func add(endReleaseDate: Date?, _ fbb: inout FlatBufferBuilder) { guard let endReleaseDate = endReleaseDate else { return }; fbb.create(struct: endReleaseDate, position: VT.endReleaseDate) }
|
||||
public static func addVectorOf(tags: Offset, _ fbb: inout FlatBufferBuilder) { fbb.add(offset: tags, at: VT.tags) }
|
||||
@@ -494,13 +494,13 @@ public struct Metadata: FlatBufferTable, FlatbuffersVectorInitializable, Verifia
|
||||
public static func createMetadata(
|
||||
_ fbb: inout FlatBufferBuilder,
|
||||
titleOffset title: Offset,
|
||||
format: Format = .manga,
|
||||
format: Format? = nil,
|
||||
originalLanguageOffset originalLanguage: Offset = Offset(),
|
||||
countryOfOriginOffset countryOfOrigin: Offset = Offset(),
|
||||
publicationDemographic: PublicationDemographic = .shounen,
|
||||
status: Status = .finished,
|
||||
contentRating: ContentRating = .safe,
|
||||
source: Source = .original,
|
||||
publicationDemographic: PublicationDemographic? = nil,
|
||||
status: Status? = nil,
|
||||
contentRating: ContentRating? = nil,
|
||||
source: Source? = nil,
|
||||
startReleaseDate: Date? = nil,
|
||||
endReleaseDate: Date? = nil,
|
||||
tagsVectorOffset tags: Offset = Offset(),
|
||||
|
||||
Reference in New Issue
Block a user