mirror of
https://github.com/Team4388/RidgeScout.git
synced 2026-06-09 00:37:59 -06:00
Merge pull request #6 from Team4388/python-file-transfer
Add HTTP file transfer
This commit is contained in:
@@ -1,3 +1,9 @@
|
|||||||
|
# Python server
|
||||||
|
__pycache__/
|
||||||
|
metadata.json
|
||||||
|
api_key.txt
|
||||||
|
server_data/
|
||||||
|
|
||||||
# Gradle files
|
# Gradle files
|
||||||
.gradle/
|
.gradle/
|
||||||
build/
|
build/
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import androidx.fragment.app.Fragment;
|
|||||||
|
|
||||||
import com.ridgebotics.ridgescout.R;
|
import com.ridgebotics.ridgescout.R;
|
||||||
import com.ridgebotics.ridgescout.types.frcEvent;
|
import com.ridgebotics.ridgescout.types.frcEvent;
|
||||||
|
import com.ridgebotics.ridgescout.utility.AlertManager;
|
||||||
import com.ridgebotics.ridgescout.utility.FileEditor;
|
import com.ridgebotics.ridgescout.utility.FileEditor;
|
||||||
import com.ridgebotics.ridgescout.utility.SettingsManager;
|
import com.ridgebotics.ridgescout.utility.SettingsManager;
|
||||||
import com.ridgebotics.ridgescout.databinding.FragmentScoutingBinding;
|
import com.ridgebotics.ridgescout.databinding.FragmentScoutingBinding;
|
||||||
@@ -126,7 +127,12 @@ public class ScoutingFragment extends Fragment {
|
|||||||
binding.textName.setText("Welcome, " + SettingsManager.getUsername() + "!");
|
binding.textName.setText("Welcome, " + SettingsManager.getUsername() + "!");
|
||||||
|
|
||||||
int matchNum = SettingsManager.getMatchNum();
|
int matchNum = SettingsManager.getMatchNum();
|
||||||
int nextMatch = event.getNextTeamMatch(SettingsManager.getTeamNum(), matchNum).matchIndex;
|
int nextMatch = -1;
|
||||||
|
try {
|
||||||
|
nextMatch = event.getNextTeamMatch(SettingsManager.getTeamNum(), matchNum).matchIndex;
|
||||||
|
} catch (Exception e){
|
||||||
|
AlertManager.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
binding.textNextMatch.setText("Our next match: Match " + nextMatch);
|
binding.textNextMatch.setText("Our next match: Match " + nextMatch);
|
||||||
binding.textMatchAlliance.setText("Match: " + (matchNum+1) + ", " + SettingsManager.getAllyPos());
|
binding.textMatchAlliance.setText("Match: " + (matchNum+1) + ", " + SettingsManager.getAllyPos());
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import com.ridgebotics.ridgescout.ui.views.CustomSpinnerView;
|
|||||||
import com.ridgebotics.ridgescout.ui.views.TallyCounterView;
|
import com.ridgebotics.ridgescout.ui.views.TallyCounterView;
|
||||||
import com.ridgebotics.ridgescout.utility.DataManager;
|
import com.ridgebotics.ridgescout.utility.DataManager;
|
||||||
import com.ridgebotics.ridgescout.utility.FileEditor;
|
import com.ridgebotics.ridgescout.utility.FileEditor;
|
||||||
|
import com.ridgebotics.ridgescout.utility.SettingsManager;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -92,11 +93,13 @@ public class SettingsFragment extends Fragment {
|
|||||||
|
|
||||||
manager.addItem(new CheckboxSettingsItem(CustomEventsKey, "Custom Events"));
|
manager.addItem(new CheckboxSettingsItem(CustomEventsKey, "Custom Events"));
|
||||||
|
|
||||||
StringSettingsItem FTPServer = new StringSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPServer, "FTP Server (Sync)");
|
StringSettingsItem FTPKey = new StringSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPKey, "Sync Key");
|
||||||
|
manager.addItem(FTPKey);
|
||||||
|
StringSettingsItem FTPServer = new StringSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPServer, "Sync Server (Sync)");
|
||||||
manager.addItem(FTPServer);
|
manager.addItem(FTPServer);
|
||||||
CheckboxSettingsItem FTPSendMetaFiles = new CheckboxSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPSendMetaFiles, "Sync meta files");
|
CheckboxSettingsItem FTPSendMetaFiles = new CheckboxSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPSendMetaFiles, "⚠ Send meta files");
|
||||||
manager.addItem(FTPSendMetaFiles);
|
manager.addItem(FTPSendMetaFiles);
|
||||||
CheckboxSettingsItem FTPEnabled = new CheckboxSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPEnabled, "FTP Enabled", FTPServer, FTPSendMetaFiles);
|
CheckboxSettingsItem FTPEnabled = new CheckboxSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPEnabled, "FTP Enabled", FTPServer, FTPKey, FTPSendMetaFiles);
|
||||||
manager.addItem(FTPEnabled);
|
manager.addItem(FTPEnabled);
|
||||||
|
|
||||||
manager.addItem(new CheckboxSettingsItem(WifiModeKey, "Wifi Mode", FTPEnabled));
|
manager.addItem(new CheckboxSettingsItem(WifiModeKey, "Wifi Mode", FTPEnabled));
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
// This is now deprecated
|
||||||
// Class to synchronise data over FTP.
|
// Class to synchronise data over FTP.
|
||||||
public class FTPSync extends Thread {
|
public class FTPSync extends Thread {
|
||||||
public static final String remoteBasePath = "/RidgeScout/";
|
public static final String remoteBasePath = "/RidgeScout/";
|
||||||
|
|||||||
@@ -0,0 +1,369 @@
|
|||||||
|
package com.ridgebotics.ridgescout.ui.transfer;
|
||||||
|
|
||||||
|
import static com.ridgebotics.ridgescout.utility.FileEditor.baseDir;
|
||||||
|
|
||||||
|
import com.ridgebotics.ridgescout.utility.AlertManager;
|
||||||
|
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
|
||||||
|
import com.ridgebotics.ridgescout.utility.ByteBuilder;
|
||||||
|
import com.ridgebotics.ridgescout.utility.FileEditor;
|
||||||
|
import com.ridgebotics.ridgescout.utility.HttpGetFile;
|
||||||
|
import com.ridgebotics.ridgescout.utility.HttpPutFile;
|
||||||
|
import com.ridgebotics.ridgescout.utility.RequestTask;
|
||||||
|
import com.ridgebotics.ridgescout.utility.SettingsManager;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
// This is now deprsicated
|
||||||
|
// Class to syncronise data over FTP.
|
||||||
|
public class HttpSync extends Thread {
|
||||||
|
public static final String timestampsFilename = "timestamps";
|
||||||
|
|
||||||
|
private static final long millisTolerance = 1000;
|
||||||
|
|
||||||
|
private boolean after(Date a, Date b){
|
||||||
|
return a.getTime() - b.getTime() > millisTolerance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface onResult {
|
||||||
|
void onResult(boolean error, int upCount, int downCount);
|
||||||
|
}
|
||||||
|
public interface UpdateIndicator {
|
||||||
|
void onText(String text);
|
||||||
|
}
|
||||||
|
private static UpdateIndicator updateIndicator = text -> {};
|
||||||
|
public static String text = "";
|
||||||
|
private static void setUpdateIndicator(String m_text){
|
||||||
|
text = m_text;
|
||||||
|
updateIndicator.onText(m_text);
|
||||||
|
}
|
||||||
|
public static void setOnUpdateIndicator(UpdateIndicator m_updateIndicator){
|
||||||
|
updateIndicator = m_updateIndicator;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static onResult onResult = (error, upCount, downCount) -> {};
|
||||||
|
public static void setOnResult(onResult result){
|
||||||
|
onResult = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isRunning = false;
|
||||||
|
public static boolean getIsRunning(){return isRunning;}
|
||||||
|
|
||||||
|
public static void sync(){
|
||||||
|
// DataManager.reload_event();
|
||||||
|
HttpSync sync = new HttpSync();
|
||||||
|
|
||||||
|
sync.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int upCount = 0;
|
||||||
|
private int downCount = 0;
|
||||||
|
|
||||||
|
private class TransferFile {
|
||||||
|
public String filename;
|
||||||
|
public Date updated;
|
||||||
|
public String checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TransferFile> localFiles = new ArrayList<>();
|
||||||
|
private List<TransferFile> remoteFiles = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void await() {
|
||||||
|
while(!runningRequest.get()) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(100);
|
||||||
|
} catch (InterruptedException e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AtomicBoolean runningRequest = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
isRunning = true;
|
||||||
|
boolean sendMetaFiles = SettingsManager.getFTPSendMetaFiles();
|
||||||
|
String serverIP = SettingsManager.getFTPServer();
|
||||||
|
String serverKey = SettingsManager.getFTPKey();
|
||||||
|
|
||||||
|
setUpdateIndicator("Getting Metadata...");
|
||||||
|
|
||||||
|
// Load metadata from server
|
||||||
|
getRemoteFileMetadata(serverIP, serverKey);
|
||||||
|
|
||||||
|
if(!isRunning){
|
||||||
|
setUpdateIndicator("Error Connecting");
|
||||||
|
onResult.onResult(true, upCount, downCount);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getLocalFileMetadata();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Wait for metadata request to finish
|
||||||
|
|
||||||
|
setUpdateIndicator("Uploading 0%");
|
||||||
|
|
||||||
|
for(int i = 0; i < localFiles.size(); i++){
|
||||||
|
TransferFile localFile = localFiles.get(i);
|
||||||
|
|
||||||
|
System.out.print("LocalFile: " + localFile.filename + ", " + localFile.checksum + ", " + localFile.updated + ": ");
|
||||||
|
TransferFile remoteFile = findInFileArray(remoteFiles, localFile.filename);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(
|
||||||
|
(
|
||||||
|
sendMetaFiles || !(
|
||||||
|
localFile.filename.endsWith(".fields")
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
&& (remoteFile == null ||
|
||||||
|
(
|
||||||
|
!Objects.equals(localFile.checksum, remoteFile.checksum) &&
|
||||||
|
after(localFile.updated, remoteFile.updated)
|
||||||
|
)
|
||||||
|
)) {
|
||||||
|
uploadFile(localFile, serverIP, serverKey);
|
||||||
|
// await();
|
||||||
|
System.out.println("Uploaded");
|
||||||
|
upCount++;
|
||||||
|
}else {
|
||||||
|
System.out.println("Did not upload");
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpdateIndicator("Uploading " + (Math.floor((double) (i * 1000) / localFiles.size()) / 10) + "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpdateIndicator("Downloading 0%");
|
||||||
|
|
||||||
|
for(int i = 0; i < remoteFiles.size(); i++){
|
||||||
|
TransferFile remoteFile = remoteFiles.get(i);
|
||||||
|
|
||||||
|
System.out.print("RemoteFile: " + remoteFile.filename + ", " + remoteFile.checksum + ", " + remoteFile.updated + ": ");
|
||||||
|
TransferFile localFile = findInFileArray(localFiles, remoteFile.filename);
|
||||||
|
|
||||||
|
if(localFile == null ||
|
||||||
|
(
|
||||||
|
!Objects.equals(localFile.checksum, remoteFile.checksum) &&
|
||||||
|
after(remoteFile.updated, localFile.updated) &&
|
||||||
|
!localFile.updated.equals(remoteFile.updated)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
downloadFile(remoteFile, serverIP);
|
||||||
|
// await();
|
||||||
|
System.out.println("Downloaded");
|
||||||
|
downCount++;
|
||||||
|
} else {
|
||||||
|
System.out.println("Did not download");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setUpdateIndicator("Downloading " + (Math.floor((double) (i * 1000) / remoteFiles.size()) / 10) + "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setUpdateIndicator("Finished, " + upCount + " Up, " + downCount + " Down");
|
||||||
|
|
||||||
|
|
||||||
|
onResult.onResult(false, upCount, downCount);
|
||||||
|
isRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private TransferFile findInFileArray(List<TransferFile> files, String filename){
|
||||||
|
for(TransferFile file : files) {
|
||||||
|
if(file.filename.equals(filename))
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Date getLocalFileUtcTimestamp(File file) {
|
||||||
|
return new Date(file.lastModified());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getSHA256Hash(String filePath) throws IOException, NoSuchAlgorithmException {
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||||
|
FileInputStream fis = new FileInputStream(filePath);
|
||||||
|
byte[] byteArray = new byte[1024];
|
||||||
|
int bytesCount = 0;
|
||||||
|
|
||||||
|
while ((bytesCount = fis.read(byteArray)) != -1) {
|
||||||
|
digest.update(byteArray, 0, bytesCount);
|
||||||
|
}
|
||||||
|
fis.close();
|
||||||
|
|
||||||
|
byte[] bytes = digest.digest();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (byte b : bytes) {
|
||||||
|
sb.append(String.format("%02x", b));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getLocalFileMetadata() {
|
||||||
|
File localDir = new File(baseDir);
|
||||||
|
File[] localFileNames = localDir.listFiles();
|
||||||
|
|
||||||
|
assert localFileNames != null;
|
||||||
|
for (int i = 0; i < localFileNames.length; i++) {
|
||||||
|
File file = localFileNames[i];
|
||||||
|
|
||||||
|
if(file.isDirectory()) continue;
|
||||||
|
// Remove timestamts file
|
||||||
|
if(file.getName().equals(timestampsFilename)) continue;
|
||||||
|
|
||||||
|
TransferFile tf = new TransferFile();
|
||||||
|
tf.filename = file.getName();
|
||||||
|
tf.updated = getLocalFileUtcTimestamp(file);
|
||||||
|
try {
|
||||||
|
tf.checksum = getSHA256Hash(file.getPath());
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
localFiles.add(tf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send request to server and retrieve metadata
|
||||||
|
private void getRemoteFileMetadata(String serverURL, String serverKey) {
|
||||||
|
final RequestTask rq = new RequestTask();
|
||||||
|
runningRequest.set(false);
|
||||||
|
rq.onResult(metadata -> {
|
||||||
|
try {
|
||||||
|
JSONObject j = new JSONObject(metadata);
|
||||||
|
for (Iterator<String> it = j.keys(); it.hasNext(); ) {
|
||||||
|
String key = it.next();
|
||||||
|
|
||||||
|
JSONObject obj = j.getJSONObject(key);
|
||||||
|
|
||||||
|
TransferFile tf = new TransferFile();
|
||||||
|
tf.filename = key;
|
||||||
|
tf.updated = new Date(Long.parseLong(obj.getString("modified")));
|
||||||
|
tf.checksum = obj.getString("sha256");
|
||||||
|
|
||||||
|
remoteFiles.add(tf);
|
||||||
|
}
|
||||||
|
}catch(JSONException | NullPointerException e ) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
isRunning = false;
|
||||||
|
}
|
||||||
|
runningRequest.set(true);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
rq.execute((serverURL + "/api/metadata"), "api_key: " + serverKey);
|
||||||
|
await();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// private boolean setTimestamps(Map<String, Date> timestamps){
|
||||||
|
// try {
|
||||||
|
// ByteBuilder bb = new ByteBuilder();
|
||||||
|
// String[] filenames = timestamps.keySet().toArray(new String[0]);
|
||||||
|
//
|
||||||
|
// for(int i = 0; i < filenames.length; i++){
|
||||||
|
// bb.addString(filenames[i]);
|
||||||
|
// bb.addLong(timestamps.get(filenames[i]).getTime());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// FileEditor.writeFile(timestampsFilename, bb.build());
|
||||||
|
//
|
||||||
|
// uploadFile(new File(baseDir + timestampsFilename));
|
||||||
|
// return true;
|
||||||
|
// } catch (ByteBuilder.buildingException | IOException e) {
|
||||||
|
// AlertManager.error("Failed Syncing!", e);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private Map<String, Date> getTimestamps() {
|
||||||
|
// try {
|
||||||
|
// downloadFile(timestampsFilename, new File(baseDir + timestampsFilename));
|
||||||
|
//
|
||||||
|
// byte[] data = FileEditor.readFile(timestampsFilename);
|
||||||
|
//
|
||||||
|
// if(data == null || data.length == 0)
|
||||||
|
// return new HashMap<>();
|
||||||
|
//
|
||||||
|
// BuiltByteParser bbp = new BuiltByteParser(data);
|
||||||
|
// List<BuiltByteParser.parsedObject> pa = bbp.parse();
|
||||||
|
//
|
||||||
|
// Map<String, Date> output = new HashMap<>();
|
||||||
|
// for(int i = 0; i < pa.size(); i+=2){
|
||||||
|
//// System.out.println((long) pa.get(i).get());
|
||||||
|
// output.put(
|
||||||
|
// (String) pa.get(i).get(),
|
||||||
|
// new Date((long) pa.get(i+1).get())
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// return output;
|
||||||
|
//
|
||||||
|
// }catch (IOException | BuiltByteParser.byteParsingExeption e){
|
||||||
|
// AlertManager.error("Failed Syncing!", e);
|
||||||
|
// return new HashMap<>();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
void uploadFile(TransferFile tf, String serverURL, String apiKey) {
|
||||||
|
runningRequest.set(false);
|
||||||
|
HttpPutFile uploadTask = new HttpPutFile(serverURL + "/api/" + tf.filename, new File(baseDir + tf.filename), new HttpPutFile.UploadCallback() {
|
||||||
|
@Override
|
||||||
|
public void onResult(String error) {
|
||||||
|
if(error != null)
|
||||||
|
AlertManager.error(error);
|
||||||
|
runningRequest.set(true);
|
||||||
|
}
|
||||||
|
}, new String[]{
|
||||||
|
"api_key: " + apiKey,
|
||||||
|
("modified: " + tf.updated.getTime())
|
||||||
|
}); // Pass auth token if needed
|
||||||
|
|
||||||
|
uploadTask.execute();
|
||||||
|
await();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void setLocalFileTimestamp(File file, Date date) {
|
||||||
|
file.setLastModified(date.getTime());
|
||||||
|
}
|
||||||
|
void downloadFile(TransferFile tf, String serverURL) {
|
||||||
|
runningRequest.set(false);
|
||||||
|
File f = new File(baseDir + tf.filename);
|
||||||
|
HttpGetFile uploadTask = new HttpGetFile(serverURL + "/api/" + tf.filename, f, new HttpGetFile.DownloadCallback() {
|
||||||
|
@Override
|
||||||
|
public void onResult(String error) {
|
||||||
|
if(error != null)
|
||||||
|
AlertManager.error(error);
|
||||||
|
else
|
||||||
|
setLocalFileTimestamp(f, tf.updated);
|
||||||
|
runningRequest.set(true);
|
||||||
|
|
||||||
|
}
|
||||||
|
}); // Pass auth token if needed
|
||||||
|
|
||||||
|
uploadTask.execute();
|
||||||
|
await();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -65,13 +65,13 @@ public class TransferFragment extends Fragment {
|
|||||||
|
|
||||||
binding.SyncButton.setOnClickListener(v -> {
|
binding.SyncButton.setOnClickListener(v -> {
|
||||||
binding.SyncButton.setEnabled(false);
|
binding.SyncButton.setEnabled(false);
|
||||||
FTPSync.sync();
|
HttpSync.sync();
|
||||||
});
|
});
|
||||||
|
|
||||||
if(FTPSync.getIsRunning())
|
if(HttpSync.getIsRunning())
|
||||||
binding.SyncButton.setEnabled(false);
|
binding.SyncButton.setEnabled(false);
|
||||||
|
|
||||||
FTPSync.setOnResult((error, upcount, downcount) -> {
|
HttpSync.setOnResult((error, upcount, downcount) -> {
|
||||||
if (getActivity() != null)
|
if (getActivity() != null)
|
||||||
getActivity().runOnUiThread(() -> {
|
getActivity().runOnUiThread(() -> {
|
||||||
binding.SyncButton.setEnabled(true);
|
binding.SyncButton.setEnabled(true);
|
||||||
@@ -79,8 +79,8 @@ public class TransferFragment extends Fragment {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
binding.syncIndicator.setText(FTPSync.text);
|
binding.syncIndicator.setText(HttpSync.text);
|
||||||
FTPSync.setOnUpdateIndicator(text -> {if(getActivity() != null) getActivity().runOnUiThread(() -> binding.syncIndicator.setText(text));});
|
HttpSync.setOnUpdateIndicator(text -> {if(getActivity() != null) getActivity().runOnUiThread(() -> binding.syncIndicator.setText(text));});
|
||||||
|
|
||||||
if(evcode.equals("unset")){
|
if(evcode.equals("unset")){
|
||||||
binding.noEventError.setVisibility(View.VISIBLE);
|
binding.noEventError.setVisibility(View.VISIBLE);
|
||||||
|
|||||||
@@ -0,0 +1,168 @@
|
|||||||
|
package com.ridgebotics.ridgescout.utility;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
public class HttpGetFile extends AsyncTask<Void, Integer, File> {
|
||||||
|
|
||||||
|
public interface DownloadCallback {
|
||||||
|
void onResult(String error);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String downloadUrl;
|
||||||
|
private File destinationFile;
|
||||||
|
private DownloadCallback callback;
|
||||||
|
private String errorMessage;
|
||||||
|
public HttpGetFile(String downloadUrl, File destinationFile, DownloadCallback callback) {
|
||||||
|
this.downloadUrl = downloadUrl;
|
||||||
|
this.destinationFile = destinationFile;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected File doInBackground(Void... voids) {
|
||||||
|
HttpURLConnection connection = null;
|
||||||
|
InputStream inputStream = null;
|
||||||
|
FileOutputStream outputStream = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
URL url = new URL(downloadUrl);
|
||||||
|
connection = (HttpURLConnection) url.openConnection();
|
||||||
|
|
||||||
|
// Configure connection for GET request
|
||||||
|
connection.setRequestMethod("GET");
|
||||||
|
connection.setDoInput(true);
|
||||||
|
connection.setConnectTimeout(30000); // 30 seconds
|
||||||
|
connection.setReadTimeout(60000); // 60 seconds
|
||||||
|
|
||||||
|
connection.connect();
|
||||||
|
|
||||||
|
// Check response code
|
||||||
|
int responseCode = connection.getResponseCode();
|
||||||
|
if (responseCode < 200 || responseCode >= 300) {
|
||||||
|
String errorResponse = readErrorResponse(connection);
|
||||||
|
errorMessage = "Download failed. Response code: " + responseCode +
|
||||||
|
(errorResponse != null ? ". Error: " + errorResponse : "");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get file size for progress tracking
|
||||||
|
long fileSize = connection.getContentLengthLong();
|
||||||
|
if (fileSize == -1) {
|
||||||
|
fileSize = connection.getContentLength(); // fallback for older API
|
||||||
|
}
|
||||||
|
|
||||||
|
inputStream = connection.getInputStream();
|
||||||
|
|
||||||
|
// Create destination file and directories if needed
|
||||||
|
if (destinationFile.getParentFile() != null && !destinationFile.getParentFile().exists()) {
|
||||||
|
if (!destinationFile.getParentFile().mkdirs()) {
|
||||||
|
errorMessage = "Failed to create destination directory: " + destinationFile.getParentFile().getAbsolutePath();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream = new FileOutputStream(destinationFile);
|
||||||
|
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
long downloadedBytes = 0;
|
||||||
|
int bytesRead;
|
||||||
|
|
||||||
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||||
|
if (isCancelled()) {
|
||||||
|
deletePartialFile();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
|
downloadedBytes += bytesRead;
|
||||||
|
|
||||||
|
// Update progress if file size is known
|
||||||
|
if (fileSize > 0) {
|
||||||
|
int progress = (int) ((downloadedBytes * 100) / fileSize);
|
||||||
|
publishProgress(progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.flush();
|
||||||
|
// Log.d(TAG, "Download successful. File saved to: " + destinationFile.getAbsolutePath());
|
||||||
|
return destinationFile;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
errorMessage = "Download error: " + e.getMessage();
|
||||||
|
// Log.e(TAG, errorMessage, e);
|
||||||
|
deletePartialFile();
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
closeResources(inputStream, outputStream, connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(File result) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onResult(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCancelled() {
|
||||||
|
deletePartialFile();
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onResult("Download cancelled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readErrorResponse(HttpURLConnection connection) {
|
||||||
|
try {
|
||||||
|
InputStream errorStream = connection.getErrorStream();
|
||||||
|
if (errorStream != null) {
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(errorStream));
|
||||||
|
StringBuilder response = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
response.append(line);
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
return response.toString();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
// Log.e(TAG, "Error reading error response", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deletePartialFile() {
|
||||||
|
if (destinationFile != null && destinationFile.exists()) {
|
||||||
|
if (destinationFile.delete()) {
|
||||||
|
// Log.d(TAG, "Partial download file deleted");
|
||||||
|
} else {
|
||||||
|
// Log.w(TAG, "Failed to delete partial download file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeResources(InputStream inputStream, OutputStream outputStream, HttpURLConnection connection) {
|
||||||
|
try {
|
||||||
|
if (inputStream != null) inputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
// Log.e(TAG, "Error closing input stream", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (outputStream != null) outputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
// Log.e(TAG, "Error closing output stream", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection != null) {
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
package com.ridgebotics.ridgescout.utility;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
//import android.util.Log;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
public class HttpPutFile extends AsyncTask<Void, Integer, Boolean> {
|
||||||
|
|
||||||
|
// private static final String TAG = "FileUploadTask";
|
||||||
|
|
||||||
|
public interface UploadCallback {
|
||||||
|
void onResult(String error);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String uploadUrl;
|
||||||
|
private File fileToUpload;
|
||||||
|
private UploadCallback callback;
|
||||||
|
private String errorMessage;
|
||||||
|
private String[] headers;
|
||||||
|
|
||||||
|
public HttpPutFile(String uploadUrl, File fileToUpload, UploadCallback callback, String[] headers) {
|
||||||
|
this.uploadUrl = uploadUrl;
|
||||||
|
this.fileToUpload = fileToUpload;
|
||||||
|
this.callback = callback;
|
||||||
|
this.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... voids) {
|
||||||
|
HttpURLConnection connection = null;
|
||||||
|
InputStream fileInputStream = null;
|
||||||
|
OutputStream outputStream = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!fileToUpload.exists()) {
|
||||||
|
errorMessage = "File does not exist: " + fileToUpload.getAbsolutePath();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
URL url = new URL(uploadUrl);
|
||||||
|
connection = (HttpURLConnection) url.openConnection();
|
||||||
|
|
||||||
|
// Configure connection for PUT request
|
||||||
|
connection.setRequestMethod("PUT");
|
||||||
|
connection.setDoOutput(true);
|
||||||
|
connection.setDoInput(true);
|
||||||
|
connection.setUseCaches(false);
|
||||||
|
connection.setRequestProperty("Content-Type", "application/octet-stream");
|
||||||
|
connection.setRequestProperty("Content-Length", String.valueOf(fileToUpload.length()));
|
||||||
|
connection.setConnectTimeout(30000); // 30 seconds
|
||||||
|
connection.setReadTimeout(60000); // 60 seconds
|
||||||
|
|
||||||
|
for(int i = 0; i < headers.length; i++){
|
||||||
|
String[] split = headers[i].split(": ");
|
||||||
|
connection.setRequestProperty(split[0], split[1]);
|
||||||
|
}
|
||||||
|
connection.connect();
|
||||||
|
|
||||||
|
outputStream = connection.getOutputStream();
|
||||||
|
fileInputStream = new FileInputStream(fileToUpload);
|
||||||
|
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
long totalBytes = fileToUpload.length();
|
||||||
|
long uploadedBytes = 0;
|
||||||
|
int bytesRead;
|
||||||
|
|
||||||
|
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
|
||||||
|
if (isCancelled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
|
uploadedBytes += bytesRead;
|
||||||
|
|
||||||
|
// Update progress
|
||||||
|
int progress = (int) ((uploadedBytes * 100) / totalBytes);
|
||||||
|
publishProgress(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.flush();
|
||||||
|
|
||||||
|
// Check response code
|
||||||
|
int responseCode = connection.getResponseCode();
|
||||||
|
if (responseCode >= 200 && responseCode < 300) {
|
||||||
|
// Log.d(TAG, "Upload successful. Response code: " + responseCode);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// Read error response if available
|
||||||
|
String errorResponse = readErrorResponse(connection);
|
||||||
|
errorMessage = "Upload failed. Response code: " + responseCode +
|
||||||
|
(errorResponse != null ? ". Error: " + errorResponse : "");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
errorMessage = "Upload error: " + e.getMessage();
|
||||||
|
// Log.e(TAG, errorMessage, e);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
closeResources(fileInputStream, outputStream, connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean success) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onResult(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCancelled() {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onResult("Upload cancelled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readErrorResponse(HttpURLConnection connection) {
|
||||||
|
try {
|
||||||
|
InputStream errorStream = connection.getErrorStream();
|
||||||
|
if (errorStream != null) {
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(errorStream));
|
||||||
|
StringBuilder response = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
response.append(line);
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
return response.toString();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
// Log.e(TAG, "Error reading error response", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeResources(InputStream inputStream, OutputStream outputStream, HttpURLConnection connection) {
|
||||||
|
try {
|
||||||
|
if (inputStream != null) inputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
// Log.e(TAG, "Error closing input stream", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (outputStream != null) outputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
// Log.e(TAG, "Error closing output stream", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection != null) {
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ public class SettingsManager {
|
|||||||
public static final String BtUUIDKey = "bt_uuid";
|
public static final String BtUUIDKey = "bt_uuid";
|
||||||
public static final String FTPEnabled = "ftp_enabled";
|
public static final String FTPEnabled = "ftp_enabled";
|
||||||
public static final String FTPServer = "ftp_server";
|
public static final String FTPServer = "ftp_server";
|
||||||
|
public static final String FTPKey = "ftp_key";
|
||||||
public static final String FTPSendMetaFiles = "ftp_send_meta_files";
|
public static final String FTPSendMetaFiles = "ftp_send_meta_files";
|
||||||
|
|
||||||
public static final String EnableQuickAllianceChangeKey = "enable_quick_alliance_change";
|
public static final String EnableQuickAllianceChangeKey = "enable_quick_alliance_change";
|
||||||
@@ -53,7 +54,8 @@ public class SettingsManager {
|
|||||||
hm.put(TeamsDataModeKey, 0);
|
hm.put(TeamsDataModeKey, 0);
|
||||||
hm.put(BtUUIDKey, UUID.randomUUID().toString());
|
hm.put(BtUUIDKey, UUID.randomUUID().toString());
|
||||||
hm.put(FTPEnabled, false);
|
hm.put(FTPEnabled, false);
|
||||||
hm.put(FTPServer, "0.0.0.0");
|
hm.put(FTPServer, "http://127.0.0.1:8080");
|
||||||
|
hm.put(FTPKey, "5uper_5ecure_k3y");
|
||||||
hm.put(FTPSendMetaFiles, false);
|
hm.put(FTPSendMetaFiles, false);
|
||||||
hm.put(EnableQuickAllianceChangeKey, false);
|
hm.put(EnableQuickAllianceChangeKey, false);
|
||||||
hm.put(CustomEventsKey, false);
|
hm.put(CustomEventsKey, false);
|
||||||
@@ -131,6 +133,9 @@ public class SettingsManager {
|
|||||||
public static String getFTPServer(){return prefs.getString( FTPServer, (String) defaults.get(FTPServer));}
|
public static String getFTPServer(){return prefs.getString( FTPServer, (String) defaults.get(FTPServer));}
|
||||||
public static void setFTPServer(String str){ getEditor().putString( FTPServer,str).apply();}
|
public static void setFTPServer(String str){ getEditor().putString( FTPServer,str).apply();}
|
||||||
|
|
||||||
|
public static String getFTPKey(){return prefs.getString( FTPKey, (String) defaults.get(FTPKey));}
|
||||||
|
public static void setFTPKey(String str){ getEditor().putString( FTPKey,str).apply();}
|
||||||
|
|
||||||
public static boolean getFTPSendMetaFiles(){return prefs.getBoolean(FTPSendMetaFiles, (boolean) defaults.get(FTPSendMetaFiles));}
|
public static boolean getFTPSendMetaFiles(){return prefs.getBoolean(FTPSendMetaFiles, (boolean) defaults.get(FTPSendMetaFiles));}
|
||||||
public static void setFTPSendMetaFiles(boolean bool){getEditor().putBoolean(FTPSendMetaFiles,bool).apply();}
|
public static void setFTPSendMetaFiles(boolean bool){getEditor().putBoolean(FTPSendMetaFiles,bool).apply();}
|
||||||
|
|
||||||
|
|||||||
+127
@@ -0,0 +1,127 @@
|
|||||||
|
from ast import mod
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import hashlib
|
||||||
|
from datetime import datetime
|
||||||
|
from bottle import Bottle, run, get, put, static_file, response, request,HTTPResponse
|
||||||
|
from random import SystemRandom
|
||||||
|
|
||||||
|
from utils import *
|
||||||
|
|
||||||
|
app = Bottle()
|
||||||
|
|
||||||
|
file_metadata = {}
|
||||||
|
|
||||||
|
def save_metadata():
|
||||||
|
global file_metadata
|
||||||
|
write(METADATA_PATH4, json.dumps(file_metadata))
|
||||||
|
|
||||||
|
def load_metadata():
|
||||||
|
global file_metadata
|
||||||
|
data = read(METADATA_PATH4)
|
||||||
|
if data is not None:
|
||||||
|
file_metadata = json.loads(data)
|
||||||
|
|
||||||
|
api_key = None
|
||||||
|
cryptogen = SystemRandom()
|
||||||
|
|
||||||
|
def aquire_key():
|
||||||
|
global api_key
|
||||||
|
global cryptogen
|
||||||
|
|
||||||
|
if api_key is None:
|
||||||
|
try:
|
||||||
|
api_key = read(API_KEY_PATH).decode("utf-8").strip()
|
||||||
|
except:
|
||||||
|
ran = cryptogen.randrange(10**80)
|
||||||
|
api_key = "%064x" % ran
|
||||||
|
write(API_KEY_PATH, api_key)
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def list_html():
|
||||||
|
global file_metadata
|
||||||
|
load_metadata()
|
||||||
|
|
||||||
|
content = '<html><body><table><tr>'
|
||||||
|
for heading in ['File', 'Size', 'Modified', 'Sha256']:
|
||||||
|
content += f'<th>{heading}</th>'
|
||||||
|
content += "</tr>"
|
||||||
|
|
||||||
|
print(file_metadata)
|
||||||
|
|
||||||
|
for filename in file_metadata.keys():
|
||||||
|
content += "<tr>"
|
||||||
|
content += f'<td><a href="/api/{filename}">{filename}</a></td>'
|
||||||
|
content += f'<td>{file_metadata[filename]["size"]}B</td>'
|
||||||
|
content += f'<td>{file_metadata[filename]["modified"]}</td>'
|
||||||
|
content += f'<td>{file_metadata[filename]["sha256"]}</td>'
|
||||||
|
content += "</tr>"
|
||||||
|
|
||||||
|
content += '</table></body></html>'
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/metadata')
|
||||||
|
def metadata():
|
||||||
|
global file_metadata
|
||||||
|
load_metadata()
|
||||||
|
response.content_type = 'application/json'
|
||||||
|
return json.dumps(file_metadata)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/<filename>', method='PUT')
|
||||||
|
def upload(filename):
|
||||||
|
global api_key
|
||||||
|
try:
|
||||||
|
sentkey = request.headers[API_KEY_HEADER]
|
||||||
|
if sentkey != api_key:
|
||||||
|
return HTTPResponse(status=403, body=f"Invalid Key")
|
||||||
|
except:
|
||||||
|
return HTTPResponse(status=403, body="You must specify an 'api_key' header")
|
||||||
|
|
||||||
|
|
||||||
|
global file_metadata
|
||||||
|
load_metadata()
|
||||||
|
|
||||||
|
data = request.body.read()
|
||||||
|
|
||||||
|
try:
|
||||||
|
modified = request.headers[MODIFIED_HEADER]
|
||||||
|
except:
|
||||||
|
modified = (datetime.now() - datetime(1970, 1, 1)).total_seconds()
|
||||||
|
|
||||||
|
sha256_hash = hashlib.sha256()
|
||||||
|
sha256_hash.update(data)
|
||||||
|
|
||||||
|
file_metadata[filename] = {
|
||||||
|
'size': len(data),
|
||||||
|
'modified': modified,
|
||||||
|
'sha256': sha256_hash.hexdigest()
|
||||||
|
}
|
||||||
|
|
||||||
|
save_metadata()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
write(os.path.join(DATA_ROOT, filename), data)
|
||||||
|
# save_metadata()
|
||||||
|
# response.content_type = 'application/json'
|
||||||
|
# return json.dumps(file_metadata)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/<filename>')
|
||||||
|
def download(filename):
|
||||||
|
|
||||||
|
data = read(os.path.join(DATA_ROOT, filename))
|
||||||
|
|
||||||
|
if data is not None:
|
||||||
|
response.content_type = 'application/octet-stream'
|
||||||
|
return data
|
||||||
|
else:
|
||||||
|
HTTPResponse(status=404, body="File not found")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
mkdir(DATA_ROOT)
|
||||||
|
aquire_key()
|
||||||
|
app.run(host='0.0.0.0', port=8080)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
bottle
|
||||||
|
random
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
ROOT = os.path.dirname(__file__)
|
||||||
|
DATA_ROOT = os.path.join(os.path.dirname(__file__), 'server_data')
|
||||||
|
|
||||||
|
METADATA_PATH4 = os.path.join(ROOT, 'metadata.json')
|
||||||
|
API_KEY_PATH = os.path.join(ROOT, 'api_key.txt')
|
||||||
|
|
||||||
|
MODIFIED_HEADER = 'modified'
|
||||||
|
API_KEY_HEADER = 'api_key'
|
||||||
|
|
||||||
|
def mkdir(path):
|
||||||
|
if not os.path.exists(path):
|
||||||
|
os.makedirs(path)
|
||||||
|
|
||||||
|
def ls(path):
|
||||||
|
try:
|
||||||
|
return os.listdir(path)
|
||||||
|
except:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def read(path):
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
return f.read()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error reading file {path}: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def write(path, data):
|
||||||
|
if not os.path.exists(path):
|
||||||
|
with open(path, mode='ab'): pass
|
||||||
|
|
||||||
|
if isinstance(data, str):
|
||||||
|
data = str.encode(data)
|
||||||
|
|
||||||
|
with open(path, 'wb') as f:
|
||||||
|
f.write(data)
|
||||||
Reference in New Issue
Block a user