Skip to content

Commit f5221ad

Browse files
authored
Update app.py
1 parent aa2d463 commit f5221ad

File tree

1 file changed

+64
-74
lines changed

1 file changed

+64
-74
lines changed

app.py

Lines changed: 64 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
SYSTEM_PASSWORD = "001Arma!23"
1616
DB_FILE = "portal_data.json"
1717

18-
# --- DATABASE FUNCTIONS (PERSISTENCE) ---
18+
# --- DATABASE FUNCTIONS ---
1919
def load_db():
2020
if not os.path.exists(DB_FILE):
2121
default_data = {
@@ -26,7 +26,7 @@ def load_db():
2626
"events": [],
2727
"tutorials": [],
2828
"announcements": [],
29-
"mod_library": [] # New storage for Mod JSONs
29+
"mod_library": []
3030
}
3131
with open(DB_FILE, 'w') as f:
3232
json.dump(default_data, f)
@@ -35,7 +35,7 @@ def load_db():
3535
try:
3636
with open(DB_FILE, 'r') as f:
3737
data = json.load(f)
38-
# Migration checks
38+
# Ensure keys exist
3939
if "usernames" not in data: data["usernames"] = {}
4040
if "mod_library" not in data: data["mod_library"] = []
4141
return data
@@ -48,48 +48,44 @@ def save_db(data):
4848

4949
DB = load_db()
5050

51-
# --- HELPER: WORKSHOP SCRAPER ---
51+
# --- HELPER: WORKSHOP SCRAPER (UPDATED FOR IMAGES) ---
5252
def fetch_mod_details(mod_input):
5353
"""
54-
Attempts to fetch Mod Name/Version from Arma Reforger Workshop.
55-
Input can be a URL or just an ID.
54+
Fetches Name, Version, and IMAGE from Arma Reforger Workshop.
5655
"""
57-
# Extract ID from URL if full link provided
5856
mod_id = mod_input.strip()
5957
if "reforger.armaplatform.com/workshop/" in mod_id:
60-
# Extract ID from URL (e.g., .../workshop/59673B6FBB95459F-BetterTracers)
6158
try:
6259
mod_id = mod_id.split("workshop/")[1].split("-")[0]
6360
except:
64-
return None, None, "Invalid URL format"
61+
return None, None, None, "Invalid URL format"
6562

6663
url = f"https://reforger.armaplatform.com/workshop/{mod_id}"
6764

6865
try:
69-
# Fake user agent to avoid immediate blocking
7066
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'}
7167
response = requests.get(url, headers=headers, timeout=5)
7268

7369
if response.status_code == 200:
7470
soup = BeautifulSoup(response.text, 'html.parser')
7571

76-
# 1. Try to find Title
77-
# Reforger workshop structure changes, but usually title is in an H1 or OG tag
78-
title = soup.find("meta", property="og:title")
79-
mod_name = title["content"] if title else "Unknown Mod"
72+
# 1. Title
73+
title_tag = soup.find("meta", property="og:title")
74+
mod_name = title_tag["content"] if title_tag else "Unknown Mod"
8075

81-
# 2. Try to find Version
82-
# This is harder to scrape without rendering JS, so we default to "1.0.0" if not found
83-
# Sometimes it's in a specific div class. We'll try a generic search.
76+
# 2. Image
77+
img_tag = soup.find("meta", property="og:image")
78+
mod_img = img_tag["content"] if img_tag else None
79+
80+
# 3. Version (Best Guess / Default)
8481
mod_version = "1.0.0"
85-
# Advanced scraping would go here, but static scraping is limited on React sites.
8682

87-
return mod_id, mod_name, mod_version
83+
return mod_id, mod_name, mod_img, mod_version
8884
else:
89-
return mod_id, None, f"Failed to reach Workshop (Status {response.status_code})"
85+
return mod_id, None, None, f"Status {response.status_code}"
9086

9187
except Exception as e:
92-
return mod_id, None, str(e)
88+
return mod_id, None, None, str(e)
9389

9490
# --- LOCAL SESSION STATE ---
9591
if "logged_in" not in st.session_state:
@@ -100,7 +96,6 @@ def fetch_mod_details(mod_input):
10096
st.session_state.page = "view_announcements"
10197
if "selected_mod_id" not in st.session_state:
10298
st.session_state.selected_mod_id = None
103-
# Editor State
10499
if "editor_content" not in st.session_state:
105100
st.session_state.editor_content = "[\n\n]"
106101

@@ -389,129 +384,124 @@ def get_mod_status():
389384
else: st.error("User not found.")
390385
st.table(pd.DataFrame(DB['role_db'].items(), columns=["Email", "Role"]))
391386

392-
# --- NEW: JSON EDITOR / MOD MANAGER PAGE ---
387+
# --- JSON EDITOR / MOD MANAGER PAGE ---
393388
elif st.session_state.page == "json_editor":
394389
st.title("📝 Mod Configuration Studio")
395390
if user_role != "SUPER_ADMIN":
396391
st.error("Access Denied.")
397392
else:
398-
# Layout: Left = Editor, Right = Library
399393
col_editor, col_library = st.columns([2, 1])
400394

401395
with col_editor:
402396
st.subheader("Config Editor")
403397
st.caption("Construct your server 'mods' array here.")
404-
# Text area connected to session state to persist changes during clicks
405398
json_text = st.text_area("JSON Output", value=st.session_state.editor_content, height=600, key="main_json_editor")
406-
# Update session state manually if user types in box
407399
st.session_state.editor_content = json_text
408-
409-
if st.button("📋 Copy to Clipboard (Manual)", help="Ctrl+A, Ctrl+C"):
410-
st.info("Select all text above and copy it.")
411400

412401
with col_library:
413402
st.subheader("📦 Mod Library")
414403

404+
# SEARCH BAR
405+
search_query = st.text_input("🔍 Search Library", placeholder="Type mod name...").lower()
406+
415407
tab_lib, tab_import = st.tabs(["Saved Mods", "Import/Fetch"])
416408

417409
# --- TAB: SAVED MODS ---
418410
with tab_lib:
419-
if not DB['mod_library']:
420-
st.info("Library is empty.")
411+
# Filter mods based on search
412+
filtered_mods = [
413+
m for m in DB['mod_library']
414+
if search_query in m.get('name', '').lower()
415+
or search_query in m.get('modId', '').lower()
416+
]
417+
418+
if not filtered_mods:
419+
if search_query: st.info("No mods found.")
420+
else: st.info("Library is empty.")
421421
else:
422-
for idx, mod in enumerate(DB['mod_library']):
422+
for mod in filtered_mods:
423423
with st.container(border=True):
424+
# Display Image if available
425+
if mod.get('image_url'):
426+
st.image(mod['image_url'], use_container_width=True)
427+
424428
st.write(f"**{mod.get('name', 'Unknown')}**")
425-
st.caption(f"ID: {mod.get('modId')} | v{mod.get('version')}")
429+
st.caption(f"v{mod.get('version')}")
430+
431+
# DISPLAY CODE BLOCK (This enables the COPY button)
432+
# We create a clean dictionary for the copy paste
433+
clean_mod = {
434+
"modId": mod.get('modId'),
435+
"name": mod.get('name'),
436+
"version": mod.get('version')
437+
}
438+
st.code(json.dumps(clean_mod, indent=4), language='json')
426439

427440
c_add, c_del = st.columns([3, 1])
428441
with c_add:
429-
if st.button("➕ Add", key=f"add_mod_{idx}"):
430-
# Logic to insert into editor
431-
# We parse current editor content to list, append, dump back
442+
if st.button("➕ Insert to Editor", key=f"add_{mod['modId']}"):
432443
try:
433-
# Clean up current content to ensure it's a list
434444
current_str = st.session_state.editor_content.strip()
435445
if not current_str: current_str = "[]"
436-
437-
# Basic heuristic: If it ends with ']', remove ']', add comma, add obj, add ']'
438-
# Or better: Try to load as json, append, dump
439-
# Note: This is a simple append logic
440-
new_snippet = json.dumps(mod, indent=4)
441-
446+
new_snippet = json.dumps(clean_mod, indent=4)
442447
if current_str.endswith("]"):
443-
# It's a list. Insert before last bracket
444-
if len(current_str) > 2: # Not empty list
445-
updated_str = current_str[:-1] + ",\n" + new_snippet + "\n]"
446-
else: # Empty list []
447-
updated_str = "[\n" + new_snippet + "\n]"
448-
else:
449-
# Just append it
450-
updated_str = current_str + ",\n" + new_snippet
451-
448+
if len(current_str) > 2: updated_str = current_str[:-1] + ",\n" + new_snippet + "\n]"
449+
else: updated_str = "[\n" + new_snippet + "\n]"
450+
else: updated_str = current_str + ",\n" + new_snippet
452451
st.session_state.editor_content = updated_str
453452
st.rerun()
454-
except Exception as e:
455-
st.error(f"Error appending: {e}")
456-
453+
except: pass
457454
with c_del:
458-
if st.button("🗑️", key=f"del_mod_{idx}"):
459-
DB['mod_library'].pop(idx)
455+
if st.button("🗑️", key=f"del_{mod['modId']}"):
456+
# Find index in main DB list (not filtered list)
457+
real_idx = DB['mod_library'].index(mod)
458+
DB['mod_library'].pop(real_idx)
460459
save_db(DB)
461460
st.rerun()
462461

463462
# --- TAB: IMPORT / FETCH ---
464463
with tab_import:
465-
st.write("**Method 1: Fetch from Workshop**")
464+
st.write("**Fetch from Workshop**")
466465
search_input = st.text_input("Workshop ID or URL", placeholder="59673B6FBB95459F")
467466
if st.button("🔍 Fetch & Save"):
468467
if search_input:
469-
mid, mname, mver = fetch_mod_details(search_input)
468+
mid, mname, mimg, mver = fetch_mod_details(search_input)
470469
if mname:
471-
new_mod = {"modId": mid, "name": mname, "version": mver}
470+
new_mod = {"modId": mid, "name": mname, "version": mver, "image_url": mimg}
472471
DB['mod_library'].append(new_mod)
473472
save_db(DB)
474473
st.success(f"Saved: {mname}")
475474
else:
476475
st.error(f"Error: {mver}")
477-
# Fallback form
478476
st.warning("Could not scrape. Enter manually below.")
479477

480-
with st.expander("Manual Entry / Fallback"):
478+
with st.expander("Manual Entry"):
481479
m_id = st.text_input("Mod ID")
482480
m_name = st.text_input("Mod Name")
483481
m_ver = st.text_input("Version", value="1.0.0")
484-
if st.button("Save Manual Entry"):
482+
if st.button("Save Manual"):
485483
if m_id and m_name:
486484
DB['mod_library'].append({"modId": m_id, "name": m_name, "version": m_ver})
487485
save_db(DB)
488486
st.success("Saved!")
489487
st.rerun()
490488

491489
st.divider()
492-
st.write("**Method 2: Batch Paste**")
493-
batch_text = st.text_area("Paste existing JSON blob here", height=150)
490+
st.write("**Batch Paste**")
491+
batch_text = st.text_area("Paste existing JSON blob", height=150)
494492
if st.button("Process Batch"):
495-
# Regex to find JSON objects with modId
496493
try:
497-
# Find all blocks that look like {"modId": ...}
498-
# This regex is loose to allow for messy input
499494
matches = re.findall(r'\{[^{}]*"modId"[^{}]*\}', batch_text, re.DOTALL)
500495
count = 0
501496
for match in matches:
502497
try:
503-
# Try to parse each match
504498
mod_obj = json.loads(match)
505499
if "modId" in mod_obj:
506500
DB['mod_library'].append(mod_obj)
507501
count += 1
508502
except: pass
509-
510503
if count > 0:
511504
save_db(DB)
512505
st.success(f"Imported {count} mods!")
513506
st.rerun()
514-
else:
515-
st.warning("No valid mod objects found.")
516-
except Exception as e:
517-
st.error(str(e))
507+
except Exception as e: st.error(str(e))

0 commit comments

Comments
 (0)