Added Gelbooru viewer

This commit is contained in:
James Shiffer 2022-06-14 00:57:43 -07:00
parent bf71d1d4f3
commit d00139ba34
No known key found for this signature in database
GPG Key ID: C0DB8774A1B3BA45
4 changed files with 126 additions and 4 deletions

View File

@ -14,6 +14,7 @@
80B3AC6628583CBD00CB7B31 /* GrabooTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80B3AC6528583CBD00CB7B31 /* GrabooTests.swift */; }; 80B3AC6628583CBD00CB7B31 /* GrabooTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80B3AC6528583CBD00CB7B31 /* GrabooTests.swift */; };
80B3AC7028583CBD00CB7B31 /* GrabooUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80B3AC6F28583CBD00CB7B31 /* GrabooUITests.swift */; }; 80B3AC7028583CBD00CB7B31 /* GrabooUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80B3AC6F28583CBD00CB7B31 /* GrabooUITests.swift */; };
80B3AC7228583CBD00CB7B31 /* GrabooUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80B3AC7128583CBD00CB7B31 /* GrabooUITestsLaunchTests.swift */; }; 80B3AC7228583CBD00CB7B31 /* GrabooUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80B3AC7128583CBD00CB7B31 /* GrabooUITestsLaunchTests.swift */; };
80B3AC8028584E3700CB7B31 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80B3AC7F28584E3700CB7B31 /* Client.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -44,6 +45,7 @@
80B3AC6B28583CBD00CB7B31 /* GrabooUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GrabooUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 80B3AC6B28583CBD00CB7B31 /* GrabooUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GrabooUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
80B3AC6F28583CBD00CB7B31 /* GrabooUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrabooUITests.swift; sourceTree = "<group>"; }; 80B3AC6F28583CBD00CB7B31 /* GrabooUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrabooUITests.swift; sourceTree = "<group>"; };
80B3AC7128583CBD00CB7B31 /* GrabooUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrabooUITestsLaunchTests.swift; sourceTree = "<group>"; }; 80B3AC7128583CBD00CB7B31 /* GrabooUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrabooUITestsLaunchTests.swift; sourceTree = "<group>"; };
80B3AC7F28584E3700CB7B31 /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -94,6 +96,7 @@
80B3AC5328583CBB00CB7B31 /* Graboo */ = { 80B3AC5328583CBB00CB7B31 /* Graboo */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
80B3AC7E28584E2100CB7B31 /* lib */,
80B3AC5428583CBB00CB7B31 /* GrabooApp.swift */, 80B3AC5428583CBB00CB7B31 /* GrabooApp.swift */,
80B3AC5628583CBB00CB7B31 /* ContentView.swift */, 80B3AC5628583CBB00CB7B31 /* ContentView.swift */,
80B3AC5828583CBD00CB7B31 /* Assets.xcassets */, 80B3AC5828583CBD00CB7B31 /* Assets.xcassets */,
@ -127,6 +130,14 @@
path = GrabooUITests; path = GrabooUITests;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
80B3AC7E28584E2100CB7B31 /* lib */ = {
isa = PBXGroup;
children = (
80B3AC7F28584E3700CB7B31 /* Client.swift */,
);
path = lib;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@ -259,6 +270,7 @@
files = ( files = (
80B3AC5728583CBB00CB7B31 /* ContentView.swift in Sources */, 80B3AC5728583CBB00CB7B31 /* ContentView.swift in Sources */,
80B3AC5528583CBB00CB7B31 /* GrabooApp.swift in Sources */, 80B3AC5528583CBB00CB7B31 /* GrabooApp.swift in Sources */,
80B3AC8028584E3700CB7B31 /* Client.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -427,7 +439,7 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@ -457,7 +469,7 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",

View File

@ -7,15 +7,59 @@
import SwiftUI import SwiftUI
final class ModelData: ObservableObject {
@Published private(set) var imgs: [GelbooruImage] = []
var searchTerms: [String]
init(searchTerms: [String]) {
self.searchTerms = searchTerms
GelbooruClient().searchTagImages(tags: searchTerms) { data, error in
self.imgs = error != nil ? [] : data!
}
}
}
struct ContentView: View { struct ContentView: View {
@EnvironmentObject var model: ModelData
let cols = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
]
var body: some View { var body: some View {
Text("Hello, world!") NavigationView {
.padding() ScrollView {
Text(model.searchTerms.joined(separator: ", "))
.font(.headline)
LazyVGrid(columns: cols, spacing: 10) {
ForEach(model.imgs, id: \.self) { pic in
VStack {
AsyncImage(url: URL(string: pic.sampleUrl)) { image in
image.resizable().aspectRatio(contentMode: .fit)
} placeholder: {
ProgressView()
}
.frame(width: 100, height: 100 * CGFloat(pic.sampleHeight)/CGFloat(pic.sampleWidth))
Text(String(pic.id))
}
}
}
}
.navigationTitle("Graboo")
}
} }
} }
struct ContentView_Previews: PreviewProvider { struct ContentView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
ContentView() ContentView()
.environmentObject(ModelData(searchTerms: ["hatsune_miku", "rating:general"]))
} }
} }

View File

@ -12,6 +12,7 @@ struct GrabooApp: App {
var body: some Scene { var body: some Scene {
WindowGroup { WindowGroup {
ContentView() ContentView()
.environmentObject(ModelData(searchTerms: ["hatsune_miku", "rating:general"]))
} }
} }
} }

65
Graboo/lib/Client.swift Normal file
View File

@ -0,0 +1,65 @@
//
// Client.swift
// Graboo
//
// Created by James Shiffer on 6/13/22.
//
import Foundation
struct GelbooruSearchResults: Decodable {
let post: [GelbooruImage];
}
struct GelbooruImage: Decodable, Hashable {
let id: Int;
let fileUrl: String;
let width: Int;
let height: Int;
let previewWidth: Int;
let previewUrl: String;
let previewHeight: Int;
let sampleUrl: String;
let sampleWidth: Int;
let sampleHeight: Int;
let tags: String;
let rating: String;
}
class GelbooruClient {
let baseUrl: String
init(baseUrl: String = "https://gelbooru.com") {
self.baseUrl = baseUrl
}
func searchTagImages(tags: [String], completionHandler: @escaping ([GelbooruImage]?, Error?) -> Void) {
let joinedTags = tags.joined(separator: " ").addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let url = URL(string: String(format: "%@/?page=dapi&s=post&q=index&json=1&tags=%@", self.baseUrl, joinedTags))!
let task = URLSession.shared.dataTask(with: url) { (data, _, error) in
guard let data = data, error == nil else {
DispatchQueue.main.async {
completionHandler(nil, error)
}
return
}
let parser = JSONDecoder()
parser.keyDecodingStrategy = .convertFromSnakeCase
var searchResults: GelbooruSearchResults
do {
searchResults = try parser.decode(GelbooruSearchResults.self, from: data)
} catch let e {
DispatchQueue.main.async {
completionHandler(nil, e)
}
return
}
DispatchQueue.main.async {
completionHandler(searchResults.post, nil)
}
}
task.resume()
}
}