From 340810398a6d4451d55d4ec4399a451624c06515 Mon Sep 17 00:00:00 2001 From: Rhuan Lima Date: Fri, 20 Dec 2024 01:19:49 -0300 Subject: [PATCH 1/2] =?UTF-8?q?Ajustando=20o=20chamado=20do=20webdriver=20?= =?UTF-8?q?e=20removendo=20as=20vers=C3=B5es=20dos=20pacotes=20para=20que?= =?UTF-8?q?=20o=20projeto=20rodasse=20corretamente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/cifraclub.py | 14 ++++++++++++-- app/requirements.txt | 6 +++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/cifraclub.py b/app/cifraclub.py index 3c2872d..601547a 100644 --- a/app/cifraclub.py +++ b/app/cifraclub.py @@ -3,14 +3,24 @@ from bs4 import BeautifulSoup from selenium import webdriver from selenium.webdriver.common.by import By -from selenium.webdriver.common.desired_capabilities import DesiredCapabilities +# from selenium.webdriver.common.desired_capabilities import DesiredCapabilities +from selenium.webdriver.firefox.options import Options CIFRACLUB_URL = "https://www.cifraclub.com.br/" class CifraClub(): """CifraClub Class""" def __init__(self): - self.driver = webdriver.Remote("http://selenium:4444/wd/hub", DesiredCapabilities.FIREFOX) + options = Options() + options.add_argument('--headless') # Executar o navegador em modo headless (opcional) + options.add_argument('--no-sandbox') # Necessário em alguns ambientes como Docker + options.add_argument('--disable-dev-shm-usage') # Evita problemas de memória compartilhada + + self.driver = webdriver.Remote( + command_executor="http://selenium:4444/wd/hub", + options=options, + # desired_capabilities=options.to_capabilities() # Garante compatibilidade + ) def cifra(self, artist: str, song: str) -> dict: """Lê a página HTML e extrai a cifra e meta dados da música.""" diff --git a/app/requirements.txt b/app/requirements.txt index 9502b39..435b042 100644 --- a/app/requirements.txt +++ b/app/requirements.txt @@ -1,3 +1,3 @@ -Flask==2.2.2 -beautifulsoup4==4.11.1 -selenium==4.6.0 +flask +bs4 +selenium \ No newline at end of file From b114be18c8f801c8f2d89e8b3253bcf111d75365 Mon Sep 17 00:00:00 2001 From: Rhuan Lima Date: Sat, 28 Dec 2024 02:42:58 -0300 Subject: [PATCH 2/2] =?UTF-8?q?Criado=20p=C3=A1gina=20para=20gerar=20lista?= =?UTF-8?q?s=20de=20musicas/=20SetList=20em=20PDF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 6 ++ app/api.py | 44 ++++++++++++-- app/get_pdf.py | 63 ++++++++++++++++++++ app/requirements.txt | 5 +- app/src/format.css | 5 ++ app/templates/index.html | 125 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 243 insertions(+), 5 deletions(-) create mode 100644 app/get_pdf.py create mode 100644 app/src/format.css create mode 100644 app/templates/index.html diff --git a/Dockerfile b/Dockerfile index 997e10a..9aa7e56 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,12 @@ FROM python:3.8-slim-buster WORKDIR /app +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + wkhtmltopdf \ + gcc \ + libpq-dev \ + && rm -rf /var/lib/apt/lists/* RUN pip install \ pylint \ diff --git a/app/api.py b/app/api.py index 7dee007..110310f 100644 --- a/app/api.py +++ b/app/api.py @@ -1,12 +1,18 @@ """API Module""" import os -from flask import Flask, json +import tempfile +from flask import Flask, json, render_template, request, jsonify, send_file from cifraclub import CifraClub +from get_pdf import create_pdf_with_columns_portrait as gen_pdf app = Flask(__name__) -@app.route('/') +@app.route("/") +def index(): + return render_template("index.html") + +@app.route('/home') def home(): """Home route""" return app.response_class( @@ -15,14 +21,44 @@ def home(): mimetype='application/json' ) + +@app.route("/submit", methods=["POST"]) +def submit(): + data = request.json + items = data["items"] + file_name = data["fileName"] + + # Criando um arquivo temporário para armazenar o PDF + with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as temp_pdf: + temp_file_path = temp_pdf.name # Caminho do arquivo temporário + + # Gerar o PDF no arquivo temporário + gen_pdf(items, temp_file_path) + + # Enviar o arquivo para o cliente + try: + return send_file( + temp_file_path, as_attachment=True, download_name=f"{file_name}.pdf" + ) + finally: + # Após o envio, deletar o arquivo temporário + if os.path.exists(temp_file_path): + os.remove(temp_file_path) + + @app.route('/artists//songs/') def get_cifra(artist, song): """Get cifra by artist and song""" cifrablub = CifraClub() + musica = cifrablub.cifra(artist, song) + + saida = f"Artista: {musica['artist']}\nMusica: {musica['name']}\nYoutube: {musica['youtube_url']}\n\n" + for cifra in musica['cifra']: + saida = saida + "\n "+cifra return app.response_class( - response=json.dumps(cifrablub.cifra(artist, song), ensure_ascii=False), + response=saida, status=200, - mimetype='application/json' + mimetype="application/json", ) if __name__ == "__main__": diff --git a/app/get_pdf.py b/app/get_pdf.py new file mode 100644 index 0000000..ec88825 --- /dev/null +++ b/app/get_pdf.py @@ -0,0 +1,63 @@ +from reportlab.lib.pagesizes import letter, A4 +from reportlab.pdfgen import canvas +from reportlab.lib.units import inch +import requests + + +def create_pdf_with_columns_portrait(data_list, output_pdf): + # Configurações básicas para o formato retrato + page_width, page_height = A4 + margin = 0.2 * inch + column_gap = 0.1 * inch + column_width = (page_width - 2 * margin - column_gap) / 2 + + # Iniciar canvas + c = canvas.Canvas(output_pdf, pagesize=A4) + + for data in data_list: + text_obj = c.beginText() + text_obj.setFont("Courier", 9) + text_obj.setTextOrigin(margin, page_height - margin) + + title = data["title"] + link = data["link"] + + # Extrair artista e música do link + artista, musica = link.rstrip("/").split("/")[-2:] + url = f"http://localhost:3000/artists/{artista}/songs/{musica}" + response = requests.get(url) + response.raise_for_status() + text = response.text + + # Escrever título (opcional) + text_obj.setFont("Courier-Bold", 10) + text_obj.textLine(f"{title}") + text_obj.setFont("Courier", 9) + text_obj.textLine("") # Linha em branco + + column = 0 + for line in text.splitlines(): + if text_obj.getY() < margin: + # Alternar coluna ou adicionar nova página + if column == 0: + column = 1 + text_obj.setTextOrigin( + margin + column_width + column_gap, page_height - margin + ) + else: + c.drawText(text_obj) + c.showPage() + text_obj = c.beginText() + text_obj.setFont("Courier", 9) + text_obj.setTextOrigin(margin, page_height - margin) + column = 0 + + # Adicionar linha ao texto + text_obj.textLine(line.rstrip()) + + # Renderizar o conteúdo do link na página atual + c.drawText(text_obj) + c.showPage() # Garantir nova página para o próximo link + + # Renderizar a última página + c.save() diff --git a/app/requirements.txt b/app/requirements.txt index 435b042..353829b 100644 --- a/app/requirements.txt +++ b/app/requirements.txt @@ -1,3 +1,6 @@ flask bs4 -selenium \ No newline at end of file +selenium +pdfkit +reportlab +requests \ No newline at end of file diff --git a/app/src/format.css b/app/src/format.css new file mode 100644 index 0000000..6b8e043 --- /dev/null +++ b/app/src/format.css @@ -0,0 +1,5 @@ + body { + column-count: 2; + column-gap: 1.5em; + /* Ajuste o espaçamento entre as colunas */ + } \ No newline at end of file diff --git a/app/templates/index.html b/app/templates/index.html new file mode 100644 index 0000000..2e1a951 --- /dev/null +++ b/app/templates/index.html @@ -0,0 +1,125 @@ + + + + + + + Formulário + + + + + + + + +
+

Criando SetList

+
+
+ + +
+
+ + +
+ +
+ +

Lista de músicas

+ + +
+ + +
+ + + + + +
+ + + + + + \ No newline at end of file