Added instructions downloader from Rebrickable.

This commit is contained in:
2025-01-22 22:41:35 +01:00
committed by Gregoo
parent d2fa72dc63
commit 1f5ce8b1f3
5 changed files with 153 additions and 2 deletions
+28 -1
View File
@@ -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 .rebrickable_set import RebrickableSet
@@ -119,6 +121,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:
+72 -1
View File
@@ -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 <img> 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'))