Skip to content

Commit

Permalink
Use new public DALL-E API
Browse files Browse the repository at this point in the history
  • Loading branch information
thenickdude committed Nov 4, 2022
1 parent 869bfa0 commit 9a91cb3
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 101 deletions.
34 changes: 17 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ https://openmemories.readthedocs.io/devices.html

I am not affiliated with OpenAI or Sony, this is not an official app.

Currently, **using this app will get your DALL-E 2 account automatically banned**, because OpenAI consider it to be a
"web scraping tool" in violation of their Terms and Conditions. So check back here after DALL-E 2 has released their
public API for a version that complies with their new terms.

![Screenshot of upload in progress](screenshot.jpg)

## Installation ##
Expand Down Expand Up @@ -88,27 +84,31 @@ Task completed successfully

## Configuration

Your Sony camera can't connect to OpenAI directly (because it only supports TLS 1.0, which is too outdated), so you need
to deploy a backend to your Amazon AWS account that will serve as a proxy for connecting to OpenAI. To create this backend,
Your Sony camera can't connect to OpenAI directly (because this Android version only supports TLS 1.0, which is too
outdated), so you need to deploy a backend to your Amazon AWS account that will serve as a proxy for connecting to
OpenAI. To create this backend,
[deploy quantum-mirror-lambda](https://github.com/Sherlock-Photography/quantum-mirror-lambda). After the deploy is
complete, it'll give you an "EndpointForCameraTokenTxt" value to use.

Create a text file in the root folder of your camera's SD Card called "TOKEN.TXT". On the first line, enter the
EndpointForCameraTokenTxt value, e.g.:
On the OpenAI website, generate a new API key for your account on this page:

https://beta.openai.com/account/api-keys

https://xxxx.cloudfront.net/
Now create a text file in the root folder of your camera's SD Card called "AI-SET.TXT", and paste this content into it:

Now on the second line of the TOKEN.TXT file you need to provide Quantum Mirror with your DALL-E credentials.
```properties
api-key=sk-xxx
endpoint=https://xxxx.cloudfront.net/
size=1024x1024
count=4
```

On the DALL-E Labs website, log in to your account, then open your browser's Developer Tools and go to the Network tab.
Click on your "My Collection page" on the DALL-E website. Then in the network tab, examine the request for the "private" URL,
and in the request parameters look for the "Authorization: sess-..." line. This token represents your logged-on
DALL-E session.
On the first line replace the `sk-xxx` with the API key you got from OpenAI.

Copy that "sess-..." token, and paste it into the second line of your TOKEN.TXT, like so:
On the second line, enter your `EndpointForCameraTokenTxt` value.

https://xxxx.cloudfront.net/
sess-xxxxxxxxxxxxxxxxxxxxx
You can edit the size and count to change how big generated images should be and how many images should be generated in
each batch. Valid sizes are 256x256, 512x512, or 1024x1024 (only). Valid counts are 1-10.

Set up a WiFi connection in your Sony settings (e.g. to a phone WiFi hotspot), since it must be connected at the time
you take your photo for it to be submitted to DALL-E.
Expand Down
124 changes: 40 additions & 84 deletions app/src/main/java/com/obsidium/bettermanual/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Properties;
import java.util.concurrent.*;

/**
Expand Down Expand Up @@ -74,6 +75,9 @@ public class MainActivity extends BaseActivity implements ActivityInterface, Cam
private String m_bearerToken;
private ExecutorService m_dalleExecutor = Executors.newSingleThreadExecutor();

private String m_genImageSize;
private int m_getImageCount;

private boolean m_facesPresent = false;

private WifiManager wifiManager;
Expand Down Expand Up @@ -125,40 +129,44 @@ public void onReceive(Context context, Intent intent) {
};

try {
loadBearerToken();
loadSettings();
} catch (Exception e) {
Log.e(TAG, "Bad TOKEN.TXT file: " + e);
showDallEProgress("Bad TOKEN.TXT file: " + e, false);
Log.e(TAG, "Bad AI-SET.TXT file: " + e);
showDallEProgress("Bad AI-SET file: " + e, false);
}
}

private void loadBearerToken() throws IOException {
File filename = new File(Environment.getExternalStorageDirectory(), "TOKEN.TXT");
private void loadSettings() throws IOException {
File filename = new File(Environment.getExternalStorageDirectory(), "AI-SET.TXT");

Log.d(TAG, "Loading settings from " + filename.toString());

Log.d(TAG, "Loading bearer token from " + filename.toString());
Properties prop = new Properties();

BufferedReader reader = new BufferedReader(new FileReader(filename));
prop.load(new FileInputStream(filename));

String line = reader.readLine().trim();
if (!line.startsWith("http")) {
throw new RuntimeException("Missing endpoint url");
String endpoint = prop.getProperty("endpoint", "");

if (!endpoint.startsWith("http")) {
throw new RuntimeException("Missing endpoint");
}

if (!line.endsWith("/")) {
line = line + "/";
if (!endpoint.endsWith("/")) {
endpoint = endpoint + "/";
}

m_endPointPrefix = line;
m_endPointPrefix = endpoint;

line = reader.readLine().trim();
String token = prop.getProperty("api-key", "");

if (line.length() < 16) {
throw new RuntimeException("Missing bearer token");
if (token.length() < 16) {
throw new RuntimeException("Missing api-key");
}

m_bearerToken = line;
m_bearerToken = token;

reader.close();
m_genImageSize = prop.getProperty("size", "1024x1024");
m_getImageCount = Integer.parseInt(prop.getProperty("count", "4"), 10);
}

protected void wifiStateChanged(int state) {
Expand Down Expand Up @@ -236,7 +244,7 @@ private String readStreamAsString(InputStream in) throws IOException {
}

private void logDallEToFile(String msg) {
File file = new File(Environment.getExternalStorageDirectory(), "DALL-E.TXT");
File file = new File(Environment.getExternalStorageDirectory(), "AI-LOG.TXT");

try {
BufferedWriter writer = new BufferedWriter(new FileWriter(file, true));
Expand All @@ -250,7 +258,7 @@ private void logDallEToFile(String msg) {
}

private JSONObject uploadDallEPicture(final byte[] bytes) throws IOException, JSONException {
URL u = new URL(m_endPointPrefix + "submitImage");
URL u = new URL(m_endPointPrefix + "submitImage?size=" + m_genImageSize + "&count=" + m_getImageCount);

HttpURLConnection c = (HttpURLConnection) u.openConnection();

Expand Down Expand Up @@ -284,29 +292,8 @@ private JSONObject uploadDallEPicture(final byte[] bytes) throws IOException, JS
return (JSONObject) new JSONTokener(readStreamAsString(in)).nextValue();
}

private JSONObject pollDallETask(final String taskID) throws IOException, JSONException {
URL u = new URL(m_endPointPrefix + "pollTask/" + taskID);

HttpURLConnection c = (HttpURLConnection) u.openConnection();

c.setUseCaches(false);
c.setRequestMethod("GET");
c.setRequestProperty("X-Authorization", "Bearer " + m_bearerToken);
c.setDoInput(true);

c.connect();

if (c.getResponseCode() != HttpURLConnection.HTTP_OK) {
throw new IOException("HTTP error " + c.getResponseCode() + " (" + c.getResponseMessage() + ")");
}

InputStream in = c.getInputStream();

return (JSONObject) new JSONTokener(readStreamAsString(in)).nextValue();
}

private InputStream startDallEImageDownload(String url) throws IOException {
URL u = new URL(url.replace("https://openailabsprodscus.blob.core.windows.net/", m_endPointPrefix + "getImage/"));
URL u = new URL(url.replace("https://oaidalleapiprodscus.blob.core.windows.net/", m_endPointPrefix + "getImage/"));

HttpURLConnection c = (HttpURLConnection) u.openConnection();

Expand Down Expand Up @@ -340,60 +327,29 @@ private void runDallE(final byte[] jpegBytes) {

try {
JSONObject responseJSON = uploadDallEPicture(jpegBytes);
String status = responseJSON.optString("status", "failed");
String taskID;
boolean success = false;

if (!("success".equals(status) || "pending".equals(status))) {
// Check for a friendly-formatted error from OpenAI
if (responseJSON.getJSONObject("error") != null) {
String message = responseJSON.getJSONObject("error").getString("message");
// Check for a friendly-formatted error from OpenAI:
JSONObject error = responseJSON.optJSONObject("error");

Log.e(TAG, "Upload error: " + message);
logDallEToFile("Upload error: " + message);
showDallEProgress("Error: " + message, false);
if (error != null) {
String message = error.getString("message");

return;
}

throw new RuntimeException("Task submit failed");
}

taskID = responseJSON.getString("id");

logDallEToFile("Submitted: https://labs.openai.com/e/" + taskID.replace("task-", ""));

for (int i = 0; i < 40; i++) {
Log.d(TAG, "DALL-e task status: " + status);

if ("pending".equals(status)) {
showDallEProgress("AI working...", false);

Thread.sleep(3000);
responseJSON = pollDallETask(taskID);

status = responseJSON.getString("status");
} else if ("succeeded".equals(status)) {
success = true;
break;
} else {
throw new RuntimeException("Task failed");
}
}
Log.e(TAG, "Upload error: " + message);
logDallEToFile("Upload error: " + message);
showDallEProgress("Error: " + message, false);

if (!success) {
throw new RuntimeException("Timed out waiting for AI");
return;
}

showDallEProgress("Downloading images...", false);

JSONArray generations = responseJSON.getJSONObject("generations").getJSONArray("data");
JSONArray generations = responseJSON.getJSONArray("data");

CountDownLatch downloadCounter = new CountDownLatch(generations.length());

for (int i = 0; i < generations.length(); i++) {
JSONObject generation = generations.getJSONObject(i);
final String imageURL = generation.getJSONObject("generation").getString("image_path");
final String imageURL = generation.getString("url");

m_handler.post(() -> {
try {
Expand Down Expand Up @@ -430,7 +386,7 @@ public void onPictureTaken(byte[] bytes, Camera camera) {
Log.d(TAG, "Captured jpeg, size: " + bytes.length);

if (m_bearerToken == null) {
showDallEProgress("No TOKEN.TXT file found!", true);
showDallEProgress("No AI-SET.TXT file found!", true);
} else if (m_facesPresent) {
showDallEProgress("Not uploading due to faces", true);
} else {
Expand Down
Empty file modified gradlew
100644 → 100755
Empty file.

0 comments on commit 9a91cb3

Please sign in to comment.