Skip to content

Commit 2444a0d

Browse files
committed
Integrated dropbox uploader script for the OTA file
Signed-off-by: Arindam Bhattacharjee <abhattacharjee717@gmail.com>
1 parent 37f226b commit 2444a0d

2 files changed

Lines changed: 131 additions & 57 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
token.txt

generate_ota.py

Lines changed: 130 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,160 @@
1-
import os
1+
# === Auto-install required packages ===
2+
import subprocess
23
import sys
4+
5+
for package in ['dropbox', 'tqdm']:
6+
try:
7+
__import__(package)
8+
except ImportError:
9+
print(f"📦 Installing missing package: {package}")
10+
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
11+
12+
# === Standard imports ===
13+
import os
314
import hashlib
415
import json
5-
import subprocess
16+
import dropbox
17+
from tqdm import tqdm
18+
from urllib.parse import urlparse, parse_qs, urlencode, urlunparse
19+
20+
# === Configuration ===
21+
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
22+
TOKEN_FILE = os.path.join(SCRIPT_DIR, "token.txt")
23+
CHUNK_SIZE = 4 * 1024 * 1024 # 4MB
24+
25+
def load_access_token():
26+
if not os.path.isfile(TOKEN_FILE):
27+
print(f"❌ '{TOKEN_FILE}' not found. Please create it and paste your Dropbox access token inside.")
28+
sys.exit(1)
29+
with open(TOKEN_FILE, "r") as f:
30+
token = f.read().strip()
31+
if not token:
32+
print(f"❌ '{TOKEN_FILE}' is empty.")
33+
sys.exit(1)
34+
return token
35+
36+
def make_direct_download_link(url):
37+
parsed = urlparse(url)
38+
query = parse_qs(parsed.query)
39+
query['dl'] = ['1']
40+
return urlunparse(parsed._replace(query=urlencode(query, doseq=True)))
41+
42+
def sha256sum(filename):
43+
h = hashlib.sha256()
44+
with open(filename, "rb") as f:
45+
for chunk in iter(lambda: f.read(8192), b""):
46+
h.update(chunk)
47+
return h.hexdigest()
48+
49+
def upload_to_dropbox(local_path, dropbox_folder, access_token):
50+
dbx = dropbox.Dropbox(access_token)
51+
filename = os.path.basename(local_path)
52+
53+
# Upload path: /<codename>/<filename>
54+
dropbox_path = f"/{dropbox_folder}/{filename}".replace("//", "/")
55+
56+
file_size = os.path.getsize(local_path)
57+
print(f"📤 Uploading '{filename}' to Dropbox path: {dropbox_path} ({file_size / (1024 * 1024):.2f} MB)")
58+
59+
with open(local_path, "rb") as f:
60+
if file_size <= CHUNK_SIZE:
61+
dbx.files_upload(f.read(), dropbox_path, mode=dropbox.files.WriteMode.overwrite)
62+
else:
63+
upload_session_start_result = dbx.files_upload_session_start(f.read(CHUNK_SIZE))
64+
cursor = dropbox.files.UploadSessionCursor(session_id=upload_session_start_result.session_id, offset=CHUNK_SIZE)
65+
commit = dropbox.files.CommitInfo(path=dropbox_path, mode=dropbox.files.WriteMode.overwrite)
66+
67+
offset = CHUNK_SIZE
68+
with tqdm(total=file_size, unit='B', unit_scale=True, desc='Uploading') as pbar:
69+
pbar.update(CHUNK_SIZE)
70+
71+
while offset < file_size:
72+
chunk = f.read(CHUNK_SIZE)
73+
if (file_size - offset) <= CHUNK_SIZE:
74+
dbx.files_upload_session_finish(chunk, cursor, commit)
75+
pbar.update(len(chunk))
76+
offset += len(chunk)
77+
else:
78+
dbx.files_upload_session_append_v2(chunk, cursor)
79+
offset += len(chunk)
80+
cursor.offset = offset
81+
pbar.update(len(chunk))
82+
83+
try:
84+
link_metadata = dbx.sharing_create_shared_link_with_settings(dropbox_path)
85+
except dropbox.exceptions.ApiError as e:
86+
if e.error.is_shared_link_already_exists():
87+
links = dbx.sharing_list_shared_links(path=dropbox_path).links
88+
if links:
89+
link_metadata = links[0]
90+
else:
91+
print("⚠️ Failed to retrieve existing shared link.")
92+
sys.exit(1)
93+
else:
94+
print(f"❌ Failed to create shared link: {e}")
95+
sys.exit(1)
96+
97+
direct_link = make_direct_download_link(link_metadata.url)
98+
print("✅ Direct Download Link:")
99+
print(direct_link)
100+
return direct_link
6101

7102
def main():
103+
access_token = load_access_token()
104+
8105
codename = input("Enter device codename (e.g. PL2, miatoll): ").strip()
9-
ota_package_dir = f"out/target/product/{codename}"
106+
ota_dir = os.path.join("out", "target", "product", codename)
10107

11-
# Try to find the OTA package matching pattern AndroidOne-*{codename}-*.zip
12108
try:
13-
files = [f for f in os.listdir(ota_package_dir) if f.startswith("AndroidOne-") and codename in f and f.endswith(".zip")]
109+
files = [f for f in os.listdir(ota_dir) if f.startswith("AndroidOne-") and codename in f and f.endswith(".zip")]
14110
except FileNotFoundError:
15-
print(f"OTA package directory not found: {ota_package_dir}")
111+
print(f"OTA directory not found: {ota_dir}")
16112
sys.exit(1)
17113

18114
if not files:
19-
print(f"OTA package file not found in {ota_package_dir}.")
20-
print(f"Ensure the file follows the pattern: AndroidOne-*{codename}-*.zip")
115+
print(f"❌ No OTA zip found in {ota_dir} matching pattern AndroidOne-*{codename}-*.zip")
21116
sys.exit(1)
22-
23-
# If multiple files matched, take the first one
117+
24118
filename = files[0]
25-
file_path = os.path.join(ota_package_dir, filename)
26-
print(f"Found OTA package: {file_path}")
119+
file_path = os.path.join(ota_dir, filename)
120+
print(f"📦 Found OTA package: {file_path}")
27121

28-
# Extract datetime from build.prop
29-
build_prop_path = os.path.join(ota_package_dir, "system", "build.prop")
122+
build_prop = os.path.join(ota_dir, "system", "build.prop")
30123
datetime = "UNKNOWN"
31-
if os.path.exists(build_prop_path):
32-
try:
33-
with open(build_prop_path, "r") as f:
34-
for line in f:
35-
if line.startswith("ro.build.date.utc="):
36-
datetime = line.strip().split("=", 1)[1]
37-
break
38-
except Exception:
39-
pass
40-
41-
# Calculate sha256 checksum
42-
def sha256sum(filename):
43-
h = hashlib.sha256()
44-
with open(filename, "rb") as f:
45-
for chunk in iter(lambda: f.read(8192), b""):
46-
h.update(chunk)
47-
return h.hexdigest()
48-
id_hash = sha256sum(file_path)
49-
50-
# Get size in bytes
51-
size = os.path.getsize(file_path)
52-
53-
version = "15" # change if needed
54-
55-
base_url = "https://storage.googleapis.com/rom"
56-
url = f"{base_url}/{filename}"
57-
58-
output_dir = "./OTA/devices"
124+
if os.path.isfile(build_prop):
125+
with open(build_prop) as f:
126+
for line in f:
127+
if line.startswith("ro.build.date.utc="):
128+
datetime = line.strip().split("=")[1]
129+
break
130+
131+
file_id = sha256sum(file_path)
132+
file_size = os.path.getsize(file_path)
133+
version = "15" # Adjust this if needed
134+
135+
dropbox_link = upload_to_dropbox(file_path, codename, access_token)
136+
137+
output_dir = os.path.join(SCRIPT_DIR, "devices")
59138
os.makedirs(output_dir, exist_ok=True)
60139
output_file = os.path.join(output_dir, f"{codename}.json")
61140

62-
# Create JSON structure
63-
data = {
141+
ota_json = {
64142
"response": [
65143
{
66144
"datetime": datetime,
67145
"filename": filename,
68-
"id": id_hash,
69-
"size": size,
70-
"url": url,
71-
"version": version,
146+
"id": file_id,
147+
"size": file_size,
148+
"url": dropbox_link,
149+
"version": version
72150
}
73151
]
74152
}
75153

76-
# Write JSON, prettified if possible
77-
try:
78-
import json
79-
with open(output_file, "w") as f:
80-
json.dump(data, f, indent=2)
81-
print(f"Minimal OTA JSON saved to {output_file}")
82-
except Exception as e:
83-
print(f"Failed to write JSON: {e}")
84-
sys.exit(1)
154+
with open(output_file, "w") as f:
155+
json.dump(ota_json, f, indent=2)
156+
157+
print(f"📄 OTA JSON saved to: {output_file}")
85158

86159
if __name__ == "__main__":
87160
main()

0 commit comments

Comments
 (0)