Feature afpshare in rascsi-web (#333)

* Define afp_dir

* Implement generic download_to_dir() method

* Display AFP share download only when netatalk is running

* Make url input required

* Better help text

* Fix server error

* Better error handling

* Typo fix

* Tweak helptext

* Show number of afpd processes running

* Tweak label
This commit is contained in:
Daniel Markstedt 2021-10-19 17:43:15 -07:00 committed by GitHub
parent 00e8855e41
commit 84ba78327b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 76 additions and 19 deletions

View File

@ -175,44 +175,42 @@ def download_file_to_iso(scsi_id, url):
try:
urllib.request.urlretrieve(url, tmp_full_path)
except (error.URLError, error.HTTPError, error.ContentTooShortError) as e:
except (error.URLError, error.HTTPError, error.ContentTooShortError, FileNotFoundError) as e:
logging.error(str(e))
return {"status": False, "msg": str(e)}
except:
return {"status": False, "msg": "Error loading the URL"}
return {"status": False, "msg": "Unknown error occurred."}
# iso_filename = make_cd(tmp_full_path, None, None) # not working yet
iso_proc = run(
["genisoimage", "-hfs", "-o", iso_filename, tmp_full_path], capture_output=True
)
if iso_proc.returncode != 0:
return {"status": False, "msg": iso_proc}
return {"status": False, "msg": str(iso_proc)}
process = attach_image(scsi_id, type="SCCD", image=iso_filename)
return {"status": process["status"], "msg": process["msg"]}
def download_image(url):
def download_to_dir(url, save_dir):
"""
Takes str url
Takes str url, str save_dir
Returns dict with boolean status and str msg
"""
import urllib.request
import urllib.error as error
server_info = get_server_info()
file_name = url.split("/")[-1]
full_path = f"{server_info['image_dir']}/{file_name}"
full_path = f"{save_dir}/{file_name}"
try:
urllib.request.urlretrieve(url, full_path)
return {"status": True, "msg": "Downloaded the URL"}
except (error.URLError, error.HTTPError, error.ContentTooShortError) as e:
except (error.URLError, error.HTTPError, error.ContentTooShortError, FileNotFoundError) as e:
logging.error(str(e))
return {"status": False, "msg": str(e)}
except:
return {"status": False, "msg": "Error loading the URL"}
return {"status": False, "msg": "Unknown error occurred."}
def write_config(file_name):

View File

@ -33,6 +33,17 @@ def running_env():
return {"git": ra_git_version, "env": pi_version}
def running_netatalk():
"""
Returns int afpd, which is the number of afpd processes currently running
"""
process = subprocess.run(["ps", "aux"], capture_output=True)
output = process.stdout.decode("utf-8")
from re import findall
afpd = findall("afpd", output)
return len(afpd)
def is_bridge_setup():
process = subprocess.run(["brctl", "show"], capture_output=True)
output = process.stdout.decode("utf-8")

View File

@ -1,7 +1,11 @@
from os import getenv, getcwd
cfg_dir = getenv("HOME", "/home/pi/") + ".config/rascsi/"
home_dir = getcwd()
# TODO: Make home_dir portable when running rascsi-web
# as a service, since the HOME env variable doesn't get set then.
home_dir = getenv("HOME", "/home/pi")
cfg_dir = f"{home_dir}/.config/rascsi/"
afp_dir = f"{home_dir}/afpshare"
web_dir = getcwd()
DEFAULT_CONFIG = "default.json"
MAX_FILE_SIZE = getenv("MAX_FILE_SIZE", 1024 * 1024 * 1024 * 4) # 4gb
@ -9,7 +13,7 @@ MAX_FILE_SIZE = getenv("MAX_FILE_SIZE", 1024 * 1024 * 1024 * 4) # 4gb
ARCHIVE_FILE_SUFFIX = ("zip",)
# File containing canonical drive properties
DRIVE_PROPERTIES_FILE = home_dir + "/drive_properties.json"
DRIVE_PROPERTIES_FILE = web_dir + "/drive_properties.json"
# File ending used for drive properties files
PROPERTIES_SUFFIX = "properties"

View File

@ -250,7 +250,7 @@
<hr/>
<details>
<summary>Download File from Web</summary>
<summary>Download File to Images</summary>
<ul>
<li>Given a URL, download that file to the <tt>{{base_dir}}</tt> directory.</li>
</ul>
@ -259,7 +259,7 @@
<table style="border: none">
<tr style="border: none">
<td style="border: none; vertical-align:top;">
<form action="/files/download_image" method="post">
<form action="/files/download_to_images" method="post">
<label for="url">URL:</label>
<input type="url" placeholder="URL" name="url" required />
<input type="submit" value="Download" />
@ -270,6 +270,33 @@
<hr/>
<details>
<summary>Download File to AppleShare</summary>
<ul>
<li>Given a URL, download that file to the <tt>{{afp_dir}}</tt> directory and share it over AFP.</li>
<li>Manage the files you download here through AppleShare on your vintage Mac.</li>
<li>Requires <a href="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing">Netatalk</a> to be installed and configured correctly for your network.</li>
</ul>
</details>
{% if netatalk_configured %}
<table style="border: none">
<tr style="border: none">
<td style="border: none; vertical-align:top;">
<form action="/files/download_to_afp" method="post">
<label for="url">URL:</label>
<input type="url" placeholder="URL" name="url" required />
<input type="submit" value="Download" />
</form>
</td>
</tr>
</table>
<p><small>{{netatalk_configured}} active connections (including the server)</small></p>
{% else %}
<p>Install <a href="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing">Netatalk</a> to use the AppleTalk File Server.
{% endif %}
<hr/>
<details>
<summary>Download File from Web and Create HFS CD (Macintosh)</summary>
<ul>

View File

@ -19,7 +19,7 @@ from file_cmds import (
delete_image,
delete_file,
unzip_file,
download_image,
download_to_dir,
write_config,
read_config,
write_drive_properties,
@ -29,6 +29,7 @@ from pi_cmds import (
shutdown_pi,
reboot_pi,
running_env,
running_netatalk,
rascsi_service,
is_bridge_setup,
disk_space,
@ -86,11 +87,13 @@ def index():
return render_template(
"index.html",
bridge_configured=is_bridge_setup(),
netatalk_configured=running_netatalk(),
devices=formatted_devices,
files=sorted_image_files,
config_files=sorted_config_files,
base_dir=server_info["image_dir"],
cfg_dir=cfg_dir,
afp_dir=afp_dir,
scsi_ids=scsi_ids,
recommended_id=recommended_id,
attached_images=attached_images,
@ -499,12 +502,26 @@ def download_file():
return redirect(url_for("index"))
@app.route("/files/download_image", methods=["POST"])
@app.route("/files/download_to_images", methods=["POST"])
def download_img():
url = request.form.get("url")
process = download_image(url)
server_info = get_server_info()
process = download_to_dir(url, server_info["image_dir"])
if process["status"] == True:
flash(f"File Downloaded from {url}")
flash(f"File Downloaded from {url} to {server_info['image_dir']}")
return redirect(url_for("index"))
else:
flash(f"Failed to download file {url}", "error")
flash(process["msg"], "error")
return redirect(url_for("index"))
@app.route("/files/download_to_afp", methods=["POST"])
def download_afp():
url = request.form.get("url")
process = download_to_dir(url, afp_dir)
if process["status"] == True:
flash(f"File Downloaded from {url} to {afp_dir}")
return redirect(url_for("index"))
else:
flash(f"Failed to download file {url}", "error")