diff --git a/AnimeGen.xcodeproj/project.pbxproj b/AnimeGen.xcodeproj/project.pbxproj index 08c28c0d..ca2f163a 100644 --- a/AnimeGen.xcodeproj/project.pbxproj +++ b/AnimeGen.xcodeproj/project.pbxproj @@ -39,6 +39,7 @@ 13BE98C02B828B8000379AB7 /* nekosmoe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13BE98BF2B828B8000379AB7 /* nekosmoe.swift */; }; 13C6CA732BD990E30046923C /* waifu-it.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C6CA722BD990E30046923C /* waifu-it.swift */; }; 13C6CA752BDA2FDF0046923C /* waifu-it-pref.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C6CA742BDA2FDF0046923C /* waifu-it-pref.swift */; }; + 13C6CA782BDA409A0046923C /* nekosbot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C6CA772BDA409A0046923C /* nekosbot.swift */; }; 13C8011D2B94C5E900BFD198 /* HmtaiSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C8011C2B94C5E900BFD198 /* HmtaiSender.swift */; }; 13C8011F2B94CAD900BFD198 /* HmtaiReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C8011E2B94CAD900BFD198 /* HmtaiReader.swift */; }; 13CC95082B8BA40100B5705E /* ApiPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13CC95072B8BA40100B5705E /* ApiPage.swift */; }; @@ -81,6 +82,7 @@ 13BE98BF2B828B8000379AB7 /* nekosmoe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = nekosmoe.swift; sourceTree = ""; }; 13C6CA722BD990E30046923C /* waifu-it.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "waifu-it.swift"; sourceTree = ""; }; 13C6CA742BDA2FDF0046923C /* waifu-it-pref.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "waifu-it-pref.swift"; sourceTree = ""; }; + 13C6CA772BDA409A0046923C /* nekosbot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = nekosbot.swift; sourceTree = ""; }; 13C8011C2B94C5E900BFD198 /* HmtaiSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HmtaiSender.swift; sourceTree = ""; }; 13C8011E2B94CAD900BFD198 /* HmtaiReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HmtaiReader.swift; sourceTree = ""; }; 13CC95072B8BA40100B5705E /* ApiPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiPage.swift; sourceTree = ""; }; @@ -202,6 +204,7 @@ 139A2E092BA4D642003F2598 /* kyoko.swift */, 13E2DC832BA5D2CF00320E2F /* purr.swift */, 13C6CA722BD990E30046923C /* waifu-it.swift */, + 13C6CA772BDA409A0046923C /* nekosbot.swift */, ); path = APIs; sourceTree = ""; @@ -322,6 +325,7 @@ 13877B1E2B82024A00251A60 /* AboutPage.swift in Sources */, 13C6CA732BD990E30046923C /* waifu-it.swift in Sources */, 13910EC52B80D5A6009BF17E /* pic-re.swift in Sources */, + 13C6CA782BDA409A0046923C /* nekosbot.swift in Sources */, 1365E8202BB0112D005E0548 /* HistoryView.swift in Sources */, 13A2B7902BD1615600B79DF7 /* Hmtai-pref.swift in Sources */, 1365E8222BB011F6005E0548 /* ImageHistory.swift in Sources */, @@ -485,7 +489,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = AnimeGen/AnimeGen.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 28; + CURRENT_PROJECT_VERSION = 30; DEVELOPMENT_TEAM = 399LMK6Q2Y; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = AnimeGen/Info.plist; @@ -520,7 +524,7 @@ CODE_SIGN_ENTITLEMENTS = AnimeGen/AnimeGen.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 28; + CURRENT_PROJECT_VERSION = 30; DEVELOPMENT_TEAM = 399LMK6Q2Y; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = AnimeGen/Info.plist; diff --git a/AnimeGen.xcodeproj/project.xcworkspace/xcuserdata/Francesco.xcuserdatad/UserInterfaceState.xcuserstate b/AnimeGen.xcodeproj/project.xcworkspace/xcuserdata/Francesco.xcuserdatad/UserInterfaceState.xcuserstate index 1f13917d..a821b437 100644 Binary files a/AnimeGen.xcodeproj/project.xcworkspace/xcuserdata/Francesco.xcuserdatad/UserInterfaceState.xcuserstate and b/AnimeGen.xcodeproj/project.xcworkspace/xcuserdata/Francesco.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/AnimeGen/APIs/nekosbot.swift b/AnimeGen/APIs/nekosbot.swift new file mode 100644 index 00000000..c98c53e6 --- /dev/null +++ b/AnimeGen/APIs/nekosbot.swift @@ -0,0 +1,102 @@ +// +// nekosbot.swift +// AnimeGen +// +// Created by cranci on 25/04/24. +// + +import UIKit + +extension ViewController { + + func loadImageFromNekoBot() { + startLoadingIndicator() + + let categoriesSFW: [String] = ["neko", "coffee", "food", "kemonomimi",] + let categoriesNSFW: [String] = ["hentai", "hkitsune", "hanal", "hthigh", "hboobs", "yaoi"] + + let isExplicitContentEnabled = UserDefaults.standard.bool(forKey: "enableExplictiCont") + let randomCategory = isExplicitContentEnabled ? categoriesNSFW.randomElement() ?? "hentai" : categoriesSFW.randomElement() ?? "hass" + + let apiEndpoint = "https://nekobot.xyz/api/image?type=\(randomCategory)" + + guard let url = URL(string: apiEndpoint) else { + if self.alert { + self.showAlert(withTitle: "Invalid URL", message: "Please wait, the API may be down.", viewController: self) + } + print("Invalid URL") + stopLoadingIndicator() + return + } + + let task = URLSession.shared.dataTask(with: url) { (data, response, error) in + DispatchQueue.main.async { + if let error = error { + if self.alert { + self.showAlert(withTitle: "Error!", message: "\(error)", viewController: self) + } + print("Error: \(error)") + self.stopLoadingIndicator() + return + } + + guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { + if self.alert { + self.showAlert(withTitle: "Invalid HTTP response", message: "Please wait, the API may be down.", viewController: self) + } + print("Invalid HTTP response") + self.stopLoadingIndicator() + return + } + + if let data = data, let jsonResponse = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any], let imageUrlString = jsonResponse["message"] as? String, let imageUrl = URL(string: imageUrlString) { + if let imageData = try? Data(contentsOf: imageUrl) { + if imageUrlString.lowercased().hasSuffix(".gif") { + if let animatedImage = UIImage.animatedImage(with: UIImage.gifData(data: imageData) ?? [], duration: 1.0) { + self.imageView.image = animatedImage + self.animateImageChange(with: animatedImage) + self.addToHistory(image: animatedImage) + } else { + print("Failed to create animated image from GIF data.") + if self.alert { + self.showAlert(withTitle: "Error!", message: "Failed to create animated image from GIF data.", viewController: self) + } + } + } else { + if let newImage = UIImage(data: imageData) { + self.imageView.image = newImage + self.addToHistory(image: newImage) + self.animateImageChange(with: newImage) + } else { + print("Failed to load image data.") + if self.alert { + self.showAlert(withTitle: "Error!", message: "Failed to load image data.", viewController: self) + } + } + } + self.currentImageURL = imageUrlString + self.tagsLabel.isHidden = false + self.updateUIWithTags([randomCategory]) + self.stopLoadingIndicator() + self.incrementCounter() + } else { + print("Failed to load image data.") + if self.alert { + self.showAlert(withTitle: "Error!", message: "Failed to load image data.", viewController: self) + } + self.stopLoadingIndicator() + } + } else { + if self.alert { + self.showAlert(withTitle: "Error!", message: "Failed to parse JSON response or missing data.", viewController: self) + } + print("Failed to parse JSON response or missing necessary data.") + self.stopLoadingIndicator() + } + } + } + + task.resume() + } + +} diff --git a/AnimeGen/AppDelegate.swift b/AnimeGen/AppDelegate.swift index 9516c0d7..6fce7d47 100644 --- a/AnimeGen/AppDelegate.swift +++ b/AnimeGen/AppDelegate.swift @@ -20,6 +20,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { UserDefaults.standard.register(defaults: ["enableTags": true]) UserDefaults.standard.register(defaults: ["enableGestures": true]) UserDefaults.standard.register(defaults: ["enableKyokobanner": true]) + UserDefaults.standard.register(defaults: ["enableDeveloperAlert": true]) // Tutorial View Prompt diff --git a/AnimeGen/Base.lproj/Main.storyboard b/AnimeGen/Base.lproj/Main.storyboard index 1a443a18..4b7fd069 100644 --- a/AnimeGen/Base.lproj/Main.storyboard +++ b/AnimeGen/Base.lproj/Main.storyboard @@ -2,6 +2,7 @@ + diff --git a/AnimeGen/Buttons/Refresh-API-Button.swift b/AnimeGen/Buttons/Refresh-API-Button.swift index 7c15515a..9d58407c 100644 --- a/AnimeGen/Buttons/Refresh-API-Button.swift +++ b/AnimeGen/Buttons/Refresh-API-Button.swift @@ -61,6 +61,9 @@ extension ViewController { case "Purr": lastImage = imageView.image loadImageFromPurr() + case "NekoBot": + lastImage = imageView.image + loadImageFromNekoBot() default: break } @@ -69,7 +72,7 @@ extension ViewController { @objc func apiButtonTapped() { let alertController = UIAlertController(title: "Select API", message: nil, preferredStyle: .actionSheet) - let apiOptions = ["Purr", "kyoko", "Hmtai" , "nekos.moe", "Nekos api", "nekos.best", "waifu.it", "waifu.pics", "waifu.im", "pic.re"] + let apiOptions = ["Purr", "kyoko", "NekoBot", "nekos.moe", "Nekos api", "nekos.best", "waifu.it", "waifu.pics", "waifu.im", "pic.re"] for option in apiOptions { let action = UIAlertAction(title: option, style: .default) { _ in self.apiButton.setTitle(option, for: .normal) diff --git a/AnimeGen/Settings/API.xcassets/NekoBot.imageset/Contents.json b/AnimeGen/Settings/API.xcassets/NekoBot.imageset/Contents.json new file mode 100644 index 00000000..11472f5b --- /dev/null +++ b/AnimeGen/Settings/API.xcassets/NekoBot.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "apple-touch-icon-removebg-preview.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AnimeGen/Settings/API.xcassets/NekoBot.imageset/apple-touch-icon-removebg-preview.png b/AnimeGen/Settings/API.xcassets/NekoBot.imageset/apple-touch-icon-removebg-preview.png new file mode 100644 index 00000000..74a595cb Binary files /dev/null and b/AnimeGen/Settings/API.xcassets/NekoBot.imageset/apple-touch-icon-removebg-preview.png differ diff --git a/AnimeGen/Settings/ApiPage.swift b/AnimeGen/Settings/ApiPage.swift index 42b06bb6..93b53fa9 100644 --- a/AnimeGen/Settings/ApiPage.swift +++ b/AnimeGen/Settings/ApiPage.swift @@ -22,6 +22,7 @@ struct ApiPage: View { APIInfo(imageName: "nekos.best", apiName: "nekos.best", url: URL(string: "https://nekos.best")!), APIInfo(imageName: "nekosapi", apiName: "nekosapi.com", url: URL(string: "https://nekosapi.com")!), APIInfo(imageName: "nekos.moe", apiName: "nekos.moe", url: URL(string: "https://nekos.moe")!), + APIInfo(imageName: "NekoBot", apiName: "NekoBot", url: URL(string: "https://nekobot.xyz")!), APIInfo(imageName: "Hmtai", apiName: "Hmtai", url: URL(string: "https://hmtai.hatsunia.cfd/endpoints")!), APIInfo(imageName: "kyoko", apiName: "Kyoko", url: URL(string: "https://api.rei.my.id/docs/ANIME/WAIFU-Generator/")!), APIInfo(imageName: "Purr", apiName: "Purr", url: URL(string: "https://purrbot.site/")!) diff --git a/AnimeGen/ViewController.swift b/AnimeGen/ViewController.swift index 9b5a3b13..0dd82ebc 100644 --- a/AnimeGen/ViewController.swift +++ b/AnimeGen/ViewController.swift @@ -336,6 +336,8 @@ class ViewController: UIViewController { loadImageFromKyoko() case "Purr": loadImageFromPurr() + case "NekoBot": + loadImageFromNekoBot() default: break diff --git a/README.md b/README.md index b85c7467..77e5599f 100644 --- a/README.md +++ b/README.md @@ -49,12 +49,13 @@ Here, you can access the IPA file for installation via TrollStore, AltStore, or | [waifu.im](https://docs.waifu.im/) | SFW/NSFW | IMG | ✅ | | [nekos.best](https://docs.nekos.best/) | SFW | IMG | ✅ | | [waifu.pics](https://waifu.pics/docs) | SFW/NSFW | IMG/GIF | ✅ | -| [Hmtai](https://hmtai.hatsunia.cfd/endpoints) | SFW/NSFW | IMG/GIF | ⚠️ | +| [Hmtai](https://hmtai.hatsunia.cfd/endpoints) | SFW/NSFW | IMG/GIF | ⚠️ | | [Nekos api](https://nekosapi.com/docs) | SFW/NSFW | IMG | ✅ | | [Nekos.moe](https://docs.nekos.moe) | SFW/NSFW | IMG | ✅ | | [Kyoko](https://api.rei.my.id/docs/ANIME/WAIFU-Generator/) | SFW/NSFW | IMG/GIF | ✅ | | [Purr Bot](https://purrbot.site/) | SFW/NSFW | IMG/GIF | ✅ | | [Waifu.it](https://waifu.it/) | SFW/NSFW | IMG/GIF | ✅ | +| [NekoBot](https://nekobot.xyz/) | SFW/NSFW | IMG/GIF | ✅ | > [!Note] > The Hmtai api is pretty slow, [why?](https://github.com/cranci1/AnimeGen/blob/main/Privacy/Hmtai.md)