diff --git a/bricktracker/instructions.py b/bricktracker/instructions.py
index 6aaa050..9813501 100644
--- a/bricktracker/instructions.py
+++ b/bricktracker/instructions.py
@@ -3,11 +3,13 @@ import logging
import os
from typing import TYPE_CHECKING
-from flask import current_app, g, url_for
+from flask import current_app, g, url_for, flash
import humanize
from werkzeug.datastructures import FileStorage
from werkzeug.utils import secure_filename
+import requests
+
from .exceptions import ErrorException
if TYPE_CHECKING:
from .set import BrickSet
@@ -112,6 +114,31 @@ class BrickInstructions(object):
logger.info('The instruction file {file} has been imported'.format(
file=self.filename
))
+
+ def download(self, href: str, /) -> None:
+ target = self.path(secure_filename(self.filename))
+
+ if os.path.isfile(target):
+ raise ErrorException('Cannot upload {target} as it already exists'.format( # noqa: E501
+ target=self.filename
+ ))
+
+ url = f"https://rebrickable.com/{href}"
+
+ response = requests.get(url)
+ if response.status_code == 200:
+ # Save the file
+ with open(target, 'wb') as file:
+ file.write(response.content)
+ print(f"Downloaded {self.filename} to {target}")
+ else:
+ print(f"Failed to download {self.filename}. Status code: {response.status_code}", 'danger')
+
+
+ # Info
+ logger.info('The instruction file {file} has been imported'.format(
+ file=self.filename
+ ))
# Compute the url for a set instructions file
def url(self, /) -> str:
diff --git a/bricktracker/views/instructions.py b/bricktracker/views/instructions.py
index 6145914..db3491a 100644
--- a/bricktracker/views/instructions.py
+++ b/bricktracker/views/instructions.py
@@ -4,7 +4,8 @@ from flask import (
redirect,
render_template,
request,
- url_for
+ url_for,
+ flash
)
from flask_login import login_required
from werkzeug.wrappers.response import Response
@@ -15,6 +16,9 @@ from ..instructions import BrickInstructions
from ..instructions_list import BrickInstructionsList
from .upload import upload_helper
+import requests
+from bs4 import BeautifulSoup
+
instructions_page = Blueprint(
'instructions',
__name__,
@@ -126,3 +130,70 @@ def do_upload() -> Response:
BrickInstructionsList(force=True)
return redirect(url_for('instructions.list'))
+
+
+# Download instructions from Rebrickable
+@instructions_page.route('/download/', methods=['GET'])
+@login_required
+@exception_handler(__file__)
+def download() -> str:
+ return render_template(
+ 'instructions.html',
+ download=True,
+ error=request.args.get('error')
+ )
+
+# Actually download an instructions file
+@instructions_page.route('/download/', methods=['POST'])
+@login_required
+@exception_handler(__file__, post_redirect='instructions.download')
+def do_download() -> Response:
+ set_id: str = request.form.get('add-set', '')
+
+ url = f"https://rebrickable.com/instructions/{set_id}"
+
+ 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'
+ }
+ response = requests.get(url, headers=headers)
+ if response.status_code != 200:
+ flash(f"Failed to load page. Status code: {response.status_code}", 'danger')
+ return redirect(url_for('instructions.download'))
+
+ # Parse the HTML content
+ soup = BeautifulSoup(response.content, 'html.parser')
+
+ # Collect all tags with "LEGO Building Instructions" in the alt attribute
+ found_tags = []
+ for a_tag in soup.find_all('a', href=True):
+ img_tag = a_tag.find('img', alt=True)
+ if img_tag and "LEGO Building Instructions" in img_tag['alt']:
+ found_tags.append((img_tag['alt'].replace('LEGO Building Instructions for ', ''), a_tag['href'])) # Save alt and href
+
+ return render_template('instructions.html', download=True, found_tags=found_tags)
+
+@instructions_page.route('/confirm_download', methods=['POST'])
+@login_required
+@exception_handler(__file__, post_redirect='instructions.download')
+def confirm_download() -> Response:
+ selected_instructions = []
+ for key in request.form:
+ if key.startswith('instruction-') and request.form.get(key) == 'on': # Checkbox is checked
+ index = key.split('-')[-1]
+ alt_text = request.form.get(f'instruction-alt-text-{index}')
+ href_text = request.form.get(f'instruction-href-text-{index}')
+ selected_instructions.append((href_text,alt_text))
+
+ if not selected_instructions:
+ flash("No instructions selected", 'danger')
+ return redirect(url_for('instructions.download'))
+
+ for href, filename in selected_instructions:
+ print(f"Downloading {filename} from {href}")
+ BrickInstructions(f"{filename}.pdf").download(href)
+
+
+ BrickInstructionsList(force=True)
+
+ #flash("Selected instructions have been downloaded", 'success')
+ return redirect(url_for('instructions.list'))
diff --git a/templates/instructions.html b/templates/instructions.html
index 54bb4c0..28734f3 100644
--- a/templates/instructions.html
+++ b/templates/instructions.html
@@ -5,6 +5,8 @@
{% block main %}
{% if upload %}
{% include 'instructions/upload.html' %}
+ {% elif download %}
+ {% include 'instructions/download.html' %}
{% elif rename %}
{% include 'instructions/rename.html' %}
{% elif delete %}
@@ -14,6 +16,7 @@
{% if g.login.is_authenticated() %}
Upload an instructions file + Download instructions from Rebrickable Refresh the instructions cache
{% endif %} diff --git a/templates/instructions/download.html b/templates/instructions/download.html new file mode 100644 index 0000000..79b3ccc --- /dev/null +++ b/templates/instructions/download.html @@ -0,0 +1,49 @@ +