Add simple flask webapp to move items from the stash into the database
This commit is contained in:
1
d2warehouse/app/__init__.py
Normal file
1
d2warehouse/app/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from d2warehouse.app.main import app
|
||||||
22
d2warehouse/app/db.py
Normal file
22
d2warehouse/app/db.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import sqlite3
|
||||||
|
from flask import g
|
||||||
|
import d2warehouse.db as base_db
|
||||||
|
|
||||||
|
|
||||||
|
def get_db():
|
||||||
|
if "db" not in g:
|
||||||
|
print("\n==========\nDB PATH", base_db._path, "\n============\n")
|
||||||
|
g.db = sqlite3.connect(
|
||||||
|
base_db._path,
|
||||||
|
detect_types=sqlite3.PARSE_DECLTYPES,
|
||||||
|
)
|
||||||
|
g.db.row_factory = sqlite3.Row
|
||||||
|
|
||||||
|
return g.db
|
||||||
|
|
||||||
|
|
||||||
|
def close_db(e=None):
|
||||||
|
db = g.pop("db", None)
|
||||||
|
|
||||||
|
if db is not None:
|
||||||
|
db.close()
|
||||||
88
d2warehouse/app/main.py
Normal file
88
d2warehouse/app/main.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
from flask import Flask, redirect, abort, render_template, request
|
||||||
|
from pathlib import Path
|
||||||
|
from d2warehouse.parser import parse_stash
|
||||||
|
import d2warehouse.db as base_db
|
||||||
|
from d2warehouse.app.db import get_db, close_db
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
STASH_FILES = {
|
||||||
|
"softcore": "SharedStashSoftCoreV2.d2i",
|
||||||
|
"hardcore": "SharedStashHardCoreV2.d2i",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def save_path() -> Path:
|
||||||
|
if "D2SAVE_PATH" in os.environ:
|
||||||
|
path = Path(os.environ["D2SAVE_PATH"])
|
||||||
|
else:
|
||||||
|
path = Path.home() / "Saved Games/Diablo II Resurrected"
|
||||||
|
|
||||||
|
if not path.exists():
|
||||||
|
raise RuntimeError("Save path `{path}` does not exist")
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
base_db.set_db_path(str(save_path() / "d2warehouse.sqlite3"))
|
||||||
|
base_db.init_db()
|
||||||
|
base_db.close_db()
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.teardown_appcontext(close_db)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def home():
|
||||||
|
return redirect("/stash/softcore", code=302)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/stash/<stash_name>")
|
||||||
|
def list_stash(stash_name: str):
|
||||||
|
if stash_name not in STASH_FILES:
|
||||||
|
abort(404)
|
||||||
|
path = save_path() / STASH_FILES[stash_name]
|
||||||
|
stash_data = path.read_bytes()
|
||||||
|
stash = parse_stash(stash_data)
|
||||||
|
return render_template("list_stash.html", stash_name=stash_name, stash=stash)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/stash/<stash_name>/remove", methods=["POST"])
|
||||||
|
def stash_remove_items(stash_name: str):
|
||||||
|
if stash_name not in STASH_FILES:
|
||||||
|
abort(404)
|
||||||
|
stash_path = save_path() / STASH_FILES[stash_name]
|
||||||
|
tmp_path = save_path() / f"{STASH_FILES[stash_name]}.temp"
|
||||||
|
if tmp_path.exists():
|
||||||
|
# TODO: Handle this condition
|
||||||
|
return "temp file exists (BAD)"
|
||||||
|
return 500
|
||||||
|
|
||||||
|
stash_data = stash_path.read_bytes()
|
||||||
|
stash = parse_stash(stash_data)
|
||||||
|
|
||||||
|
items = []
|
||||||
|
for item_location in request.form.keys():
|
||||||
|
match = re.match(r"(\d+)_(\d+)", item_location)
|
||||||
|
if not match:
|
||||||
|
# TODO: Handle this condition
|
||||||
|
return "invalid position"
|
||||||
|
tab_idx, item_idx = int(match.group(1)), int(match.group(2))
|
||||||
|
if tab_idx > len(stash.tabs) or item_idx > len(stash.tabs[tab_idx].items):
|
||||||
|
# TODO: Handle this condition
|
||||||
|
return "invalid position (2)"
|
||||||
|
item = stash.tabs[tab_idx].items[item_idx]
|
||||||
|
items.append((tab_idx, item))
|
||||||
|
|
||||||
|
# TODO: create backups
|
||||||
|
|
||||||
|
for tab_idx, item in items:
|
||||||
|
stash.tabs[tab_idx].remove(item)
|
||||||
|
tmp_path.write_bytes(stash.raw())
|
||||||
|
|
||||||
|
db = get_db()
|
||||||
|
for _, item in items:
|
||||||
|
item.write_to_db(db=db)
|
||||||
|
|
||||||
|
tmp_path.replace(stash_path)
|
||||||
|
|
||||||
|
return redirect(f"/stash/{stash_name}", code=303)
|
||||||
61
d2warehouse/app/static/style.css
Normal file
61
d2warehouse/app/static/style.css
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
body {
|
||||||
|
background-color: #000;
|
||||||
|
font-size: large;
|
||||||
|
font-family: sans-serif;
|
||||||
|
color: rgb(240, 240, 240);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item .name {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
background-color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-rare {
|
||||||
|
color: rgb(255, 255, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-unique {
|
||||||
|
color: rgb(199, 179, 119);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-set {
|
||||||
|
color: rgb(0, 252, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-runeword {
|
||||||
|
color: rgb(199, 179, 119);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
"FontColorWhite": { "r": 240, "g": 240, "b": 240, "a": 255 },
|
||||||
|
"FontColorVeryLightGray": { "r": 240, "g": 240, "b": 240, "a": 255 },
|
||||||
|
"FontColorBlack": { "r": 0, "g": 0, "b": 0, "a": 255 },
|
||||||
|
"FontColorRed": { "r": 252, "g": 70, "b": 70, "a": 255 },
|
||||||
|
"FontColorGreen": { "r": 0, "g": 252, "b": 0, "a": 255 },
|
||||||
|
"FontColorBlue": { "r": 110, "g": 110, "b": 255, "a": 255 },
|
||||||
|
"FontColorLightGold": { "r": 255, "g": 246, "b": 227, "a": 255 }, // usage: button text, setting text labels
|
||||||
|
"FontColorGoldYellow" : { "r": 199, "g": 179, "b": 119, "a": 255 }, //Usage example: panel and option subtitles
|
||||||
|
"FontColorCurrencyGold" : {"r": 209, "g": 195, "b": 120, "a": 255}, // Usage Example: Gold text
|
||||||
|
"FontColorGold": "$FontColorGoldYellow",
|
||||||
|
"FontColorDarkGold": { "r": 120, "g": 98, "b": 47, "a": 255 },
|
||||||
|
"FontColorBeige" : { "r": 204, "g": 195, "b": 176, "a": 255 },
|
||||||
|
"FontColorGray": { "r": 99, "g": 99, "b": 99, "a": 255 },
|
||||||
|
"FontColorGrey": "$FontColorGray",
|
||||||
|
"FontColorOrange": { "r": 255, "g": 168, "b": 0, "a": 255 },
|
||||||
|
"FontColorDarkGreen": { "r": 0, "g": 128, "b": 0, "a": 255 },
|
||||||
|
"FontColorYellow": { "r": 255, "g": 255, "b": 100, "a": 255 },
|
||||||
|
"FontColorLightPurple": { "r": 192, "g": 128, "b": 242, "a": 255 },
|
||||||
|
"FontColorLightTeal": { "r": 124, "g": 221, "b": 204, "a": 255 },
|
||||||
|
"FontColorLightRed": { "r": 255, "g": 148, "b": 148, "a": 255 },
|
||||||
|
"FontColorLightYellow": { "r": 255, "g": 235, "b": 164, "a": 255 },
|
||||||
|
"FontColorLightBlue": { "r": 175, "g": 183, "b": 255, "a": 255 },
|
||||||
|
"FontColorLightGray": { "r": 148, "g": 148, "b": 148, "a": 255 },
|
||||||
|
"FontColorDarkGrayBlue": { "r": 125, "g": 141, "b": 144, "a": 255 },
|
||||||
|
"FontColorDarkGrayGold" : { "r": 173, "g": 168, "b": 148, "a": 255 }, // Usage example: attribute points to spend
|
||||||
|
"FontColorTransparent": { "r": 0, "g": 0, "b": 0, "a": 0 },
|
||||||
|
"FontColorPartyOrange": { "r": 196, "g": 129, "b": 0, "a": 255 },
|
||||||
|
"FontColorPartyGreen": {"r": 79, "g": 194, "b": 56, "a": 255 },
|
||||||
|
|
||||||
14
d2warehouse/app/templates/item.html
Normal file
14
d2warehouse/app/templates/item.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<div class="item">
|
||||||
|
<input type="checkbox" name="{{tabloop.index0}}_{{itemloop.index0}}" value="remove" /> ({{tabloop.index0}}, {{itemloop.index0}})
|
||||||
|
<ul>
|
||||||
|
<li class="name color-{{item.color}}">{{item.name}}</li>
|
||||||
|
{% if item.quality and item.quality >= 5 %}
|
||||||
|
<li class="name color-{{item.color}}">{{item.basename}}</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if item.stats %}
|
||||||
|
{% for stat in item.stats %}
|
||||||
|
<li>{{stat}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
23
d2warehouse/app/templates/list_stash.html
Normal file
23
d2warehouse/app/templates/list_stash.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Shared Stash</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css" />
|
||||||
|
<head>
|
||||||
|
<body>
|
||||||
|
<form action="/stash/{{stash_name}}/remove" method="POST">
|
||||||
|
{% for tab in stash.tabs %}
|
||||||
|
<div>
|
||||||
|
{% set tabloop = loop %}
|
||||||
|
<h2>Tab {{tabloop.index}}</h2>
|
||||||
|
{% for item in tab.items %}
|
||||||
|
{% set itemloop = loop %}
|
||||||
|
{% include "item.html" %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<input type="submit" value="Remove items">
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -18,6 +18,7 @@ requires-python = ">=3.10"
|
|||||||
license = {text = "GPLv3 License"}
|
license = {text = "GPLv3 License"}
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitarray",
|
"bitarray",
|
||||||
|
"flask",
|
||||||
]
|
]
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user