web service

This commit is contained in:
thezero 2020-12-06 19:59:31 +01:00
parent 8fbe1a2bf4
commit e972291993
8 changed files with 348 additions and 7 deletions

View File

@ -9,3 +9,14 @@ services:
environment:
- CONF_FOLDER=/bot/conf/
user: 1000:1000
web:
build: .
command: python3 /bot/src/web.py
volumes:
- ./out:/bot/out
- ./conf:/bot/conf
environment:
- CONF_FOLDER=/bot/conf/
user: 1000:1000
ports:
- "5000:5000"

View File

@ -1,2 +1,3 @@
gplaycli==3.29
python-telegram-bot==13.0
Flask==1.1.2

View File

@ -53,15 +53,17 @@ class APK:
if os.path.isfile(fname):
self.file_name = fname
def check_dimension(self):
if os.path.getsize(self.file_name) > 50 * 1024 * 1023:
def check_dimension(self, split=True, remove=True):
if split and os.path.getsize(self.file_name) > 50 * 1024 * 1023:
check_call(['split', '-b', '49M', self.file_name, self.file_name])
os.remove(self.file_name)
if remove:
os.remove(self.file_name)
return glob(escape(self.file_name) + '*')
@contextmanager
def send(self):
files = self.check_dimension() # split if size >= 50MB
def send(self, split=True, remove=True):
files = self.check_dimension(split) # split if size >= 50MB
yield files
for f in files: # removing old files
os.remove(f)
if remove:
os.remove(f)

194
src/static/main.css Normal file
View File

@ -0,0 +1,194 @@
/**
* Custom CSS
*/
a {
color: #0097A7;
}
.look-form{
margin-top:20px;
margin-top:2rem;
margin-bottom:20px;
margin-bottom:2rem;
display: flex
}
.look-form label {
display:block;
font-weight:bold
}
.look-form input[type="text"] {
width:100%;
display:block;
border-radius:3px 0 0 3px;
border-radius:.3rem 0 0 .3rem;
color:#424242;
background:#fff;
border:2px solid #E0E0E0;
border:.17rem solid #E0E0E0;
transition:border-color ease-in-out .15s;
padding:11px 10px;
padding:1.1rem 1rem;
}
.look-form input[type="text"]:focus {
border-color:#0097A7;
outline:0
box-shadow: 0 0 0 .2rem #0095A570;
-webkit-box-shadow: 0 0 0 .2rem #0095A570;
}
.look-form input[type="text"] {
font-family: monospace;
}
.look-form input[type="submit"] {
height: 4rem;
width: 7rem;
z-index: 2;
background-color: #0095A5;
color: #fff;
border-radius: 0 4px 4px 0;
border-radius: 0 .4rem .4rem 0;
border: 2px solid #14854f;
text-align: center;
-webkit-transition: opacity .2s ease;
transition: opacity .2s ease;
text-decoration: none;
cursor: pointer;
}
.look-form input[type="submit"]:hover {
background-color: #0095A5;
border-color: #14854f;
opacity:.85
}
.look-form input[type="submit"]:active {
background-color: #0095A5;
border-color: #14854f;
opacity:.75
}
.look-form.error input {
border-color:#F50057
}
.console {
font-family: monospace;
}
.highlight {
color: #0097A7;
}
.look-segment {
border: 1px solid #E0E0E0;
border-top-color: rgb(224, 224, 224);
border-right-color: rgb(224, 224, 224);
border-bottom-color: rgb(224, 224, 224);
border-left-color: rgb(224, 224, 224);
border-radius: 3px;
border-radius: .3rem;
margin-top: 20px;
margin-top: 2rem;
margin-bottom: 20px;
margin-bottom: 2rem;
overflow: hidden;
}
.look-segment > .header {
background: #f5f5f5;
padding: 10px 20px;
padding: 1rem 2rem;
box-shadow: inset 0 -1px 0 #E0E0E0;
}
.look-segment > .body {
padding: 19px 20px;
padding: 1.9rem 2rem;
word-wrap: break-word;
}
.look-segment > .body > :last-child, .look-segment > .body > :last-child > :last-child {
margin-bottom: 0;
}
.look-segment > .body > :first-child, .look-segment > .body > :first-child > :first-child {
margin-top: 0;
}
.look-segment.amber {
border-color: #FFC107;
}
.look-segment.amber > .header {
box-shadow: inset 0 -1px 0 #FFC107;
background: #ffeeba;
color: #000;
}
.look-segment.green {
border-color: #4CAF50;
}
.look-segment.green > .header {
box-shadow: inset 0 -1px 0 #4CAF50;
background: #c7e7c8;
color: #000;
}
.look-label {
display: inline-block;
background: #0097A7;
color: #fff;
font-weight: bold;
border: 0;
border-radius: 3px;
border-radius: .3rem;
padding: 1px 4px;
padding: .1rem .4rem;
font-size: 14px;
text-align: center;
vertical-align: middle;
white-space: nowrap;
line-height: inherit;
}
.look-table {
width: 100%;
}
.look-table td, .look-table th {
padding: 10px;
}
.look-table a > code {
color: #0097A7;
}
.literal {
background: rgba(0,0,0,.075);
color: #000;
}
.faq .q {
font-style: italic;
color: #0077DD;
}
.legend {
font-weight: bolder;
font-size: 12px;
}
.legend .username {
color: blue;
}
.legend .password {
color: orange;
}
.legend .rounds, .legend .time_cost {
color: purple;
}
.legend .salt {
color: red;
}
.legend .hash {
color: green;
}
pre .username {
border-bottom: 2px solid blue;
padding-bottom: 4px;
/*background-color: #00008030;*/
}
pre .password {
border-bottom: 2px solid orange;
padding-bottom: 4px;
/*background-color: #ffa50030;*/
}
pre .rounds, pre .time_cost {
border-bottom: 2px solid purple;
background-color: #80008030;
}
pre .salt {
border-bottom: 2px solid red;
background-color: #ff000030;
}
pre .hash {
border-bottom: 2px solid green;
background-color: #00800030;
}

2
src/static/marx.min.css vendored Normal file

File diff suppressed because one or more lines are too long

17
src/templates/error.html Normal file
View File

@ -0,0 +1,17 @@
<!doctype html>
<html>
<head>
<title>Error</title>
<link rel="stylesheet" type="text/css" href="/static/marx.min.css">
<link rel="stylesheet" type="text/css" href="/static/main.css">
</head>
<body>
<main>
<h1>apk-downloader</h1>
<h2>Error {{code}}</h2>
<p>{{error}}</p>
<hr/>
<p>Return to the <a href="/">homepage</a> or <a href="javascript:window.history.back()">go back</a></p>
</main>
</body>
</html>

21
src/templates/home.html Normal file
View File

@ -0,0 +1,21 @@
<!doctype html>
<html>
<head>
<title>apk-downloader</title>
<link rel="stylesheet" type="text/css" href="/static/marx.min.css">
<link rel="stylesheet" type="text/css" href="/static/main.css">
</head>
<body>
<main>
<h1>apk-downloader</h1>
<p>
Download apk from the PlayStore<br/>
<br/>
<form class="look-form" action="/download/" method="POST">
<input type="text" id="package_name" name="package_name" placeholder="Placeholder...">
<input type="submit" class="button" value="Submit">
</form>
</p>
</main>
</body>
</html>

93
src/web.py Normal file
View File

@ -0,0 +1,93 @@
#!/usr/bin/env python3
import logging
import os
from flask import Flask, request, render_template, redirect, url_for, session, send_file
from download_utils import APK, BadPackageNameException
from subprocess import CalledProcessError
if "LOG_LEVEL" in os.environ:
log_level = os.environ["LOG_LEVEL"]
else:
log_level = logging.INFO
# Logging configuration.
logger = logging.getLogger(__name__)
logging.basicConfig(
format="%(asctime)s> [%(levelname)s] %(message)s",
datefmt="%d/%m/%Y %H:%M:%S",
level=log_level,
)
logging.getLogger("werkzeug").disabled = True
if os.environ.get('CONF_FOLDER') is None:
logger.error("No conf folder available")
exit(1)
TOKEN = None
with open(os.path.join(os.environ['CONF_FOLDER'], 'web.token')) as f:
TOKEN = f.read().strip()
CONF = {
'yes': True,
'verbose': False,
'tokencachefile': os.path.join(os.environ['CONF_FOLDER'], 'token.cache'),
}
CONF_FILE = os.path.join(os.environ['CONF_FOLDER'], 'gplaycli.conf')
app = Flask(__name__)
app.secret_key = TOKEN
@app.errorhandler(400)
@app.errorhandler(404)
@app.errorhandler(500)
def application_error(error):
logger.error(error)
return render_template('error.html', code=error.code, error=str(error)), error.code
@app.route("/return-file/")
def return_file():
package_name = session.get("package_name")
filename = f"{package_name}.apk"
location = session.get("filename", '')
if location != '':
location = os.path.join('/bot', location)
logging.info(f"sending {location}")
return send_file(location, attachment_filename=filename, as_attachment=True)
@app.route("/download/<package_name>", methods=["GET"])
def get_download(package_name):
return download(package_name)
@app.route("/download/", methods=["POST"])
def post_download():
package_name = request.form.get('package_name', '').strip()
return download(package_name)
def download(package_name):
logger.info("downloading {} ...".format(package_name))
apk = APK(package_name, conf=CONF, conf_file=CONF_FILE)
try:
apk.download()
with apk.send(split=False, remove=False) as files:
for f in files:
session['package_name'] = package_name
session['filename'] = f
except BadPackageNameException as e:
logger.error("Bad package name: {}".format(e))
return render_template('error.html', code=500, error="Bad package name: {}".format(e)), 500
except CalledProcessError:
logger.error("Failed to send APK file")
return render_template('error.html', code=500, error="Failed to send APK file"), 500
return redirect(url_for("return_file"))
@app.route("/", methods=["GET"], strict_slashes=False)
def home():
return render_template('home.html')
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)