diff --git a/README.org b/README.org
index 5d8d176..5e5e88f 100644
--- a/README.org
+++ b/README.org
@@ -4,16 +4,16 @@
** TODO Ordenar els resultats de la cerca de temes
** TODO Suport per a diverses organitzacions (no només jam de Sant Cugat)
** TODO Usuaris i permisos granulars
-** TODO Arreglar visualitzador de pdf, suportar més d'un visualitzador per pàgina
** Lilypond support (o similar)
*** TODO Fer cançoners "en directe"
*** DONE Suport de caràcters especials (al títol i més llocs?)
*** DONE Mostrar partitura als enllaços (resultats de cerca)
** DONE Arreglar estadístiques de temes (dos temes tocats a la mateixa sessió compten un cop)
+** TODO Arreglar visualitzador de pdf, suportar més d'un visualitzador per pàgina
* Idees
** Jams
*** Properes jams
-**** Info dels temes que es tocaràn (a la slow jam)
+**** Info dels temes que es tocaran (a la slow jam)
** Temes
*** Navegació
**** Cerca de temes
diff --git a/folkugat_web/api/tema/scores.py b/folkugat_web/api/tema/scores.py
index ce7daca..7475e0c 100644
--- a/folkugat_web/api/tema/scores.py
+++ b/folkugat_web/api/tema/scores.py
@@ -35,64 +35,12 @@ async def set_score(
score = scores_service.get_score_by_id(score_id=score_id, tema_id=tema_id)
if not score:
raise HTTPException(status_code=404, detail="Could not find lyric!")
-
- tune = lilypond_build.tune_from_tema_id(tema_id=tema_id, score_source=source)
- tune_source = lilypond_source.tune_source(tune=tune)
- pdf_result, png_result = await render_tune(tune_source=tune_source, tema_id=tema_id)
- if errors := pdf_result.error:
- new_score = dataclasses.replace(
- score,
- source=source,
- title=title,
- errors=errors,
- )
- elif png_result is None:
- raise RuntimeError(f"Received empty png_result with pdf_result: {pdf_result}")
- elif errors := png_result.error:
- new_score = dataclasses.replace(
- score,
- source=source,
- title=title,
- errors=errors,
- )
- else:
- pdf_file = pdf_result.result
- png_file = png_result.result
- new_score = dataclasses.replace(
- score,
- source=source,
- title=title,
- pdf_url=files.get_db_file_path(pdf_file) if pdf_file else None,
- img_url=files.get_db_file_path(png_file) if png_file else None,
- errors=[],
- )
+ new_score = await scores_service.render_score(score=score, title=title, source=source)
scores_service.update_score(score=new_score)
files.clean_orphan_files()
return scores_fragments.score(request=request, logged_in=logged_in, score=new_score)
-async def render_tune(
- tune_source: str,
- tema_id: int,
-) -> tuple[lilypond_render.RenderResult, lilypond_render.RenderResult | None]:
- async with files.tmp_file(content=tune_source) as source_file:
- pdf_file = files.create_tema_filename(tema_id=tema_id)
- pdf_result = await lilypond_render.render_file(
- input_file=source_file,
- output=lilypond_render.RenderOutput.PDF,
- output_file=pdf_file,
- )
- if pdf_result.error:
- return pdf_result, None
- png_file = files.create_tema_filename(tema_id=tema_id)
- png_result = await lilypond_render.render_file(
- input_file=source_file,
- output=lilypond_render.RenderOutput.PNG_CROPPED,
- output_file=png_file,
- )
- return pdf_result, png_result
-
-
@router.post("/api/tema/{tema_id}/score")
def add_score(
request: Request,
diff --git a/folkugat_web/assets/static/css/main.css b/folkugat_web/assets/static/css/main.css
index c27b609..8a566bb 100644
--- a/folkugat_web/assets/static/css/main.css
+++ b/folkugat_web/assets/static/css/main.css
@@ -693,10 +693,6 @@ video {
display: flex;
}
-.table {
- display: table;
-}
-
.hidden {
display: none;
}
@@ -899,10 +895,6 @@ video {
border-width: 0px;
}
-.border-b {
- border-bottom-width: 1px;
-}
-
.border-none {
border-style: none;
}
diff --git a/folkugat_web/assets/templates/fragments/temes/results.html b/folkugat_web/assets/templates/fragments/temes/results.html
index c699170..4341411 100644
--- a/folkugat_web/assets/templates/fragments/temes/results.html
+++ b/folkugat_web/assets/templates/fragments/temes/results.html
@@ -12,32 +12,34 @@
{{ query }}
{% endif %}
-
-
- | Nom |
- Enllaços |
- Cops tocat |
-
+
{% for tema in temes %}
-
-
+ -
+
|
-
- {% include "fragments/temes/result_links.html" %}
- |
-
- {% if tema.stats is none %}
- -
- {% else %}
- {{ tema.stats.times_played }}
- {% endif %}
- |
-
+
+ {% if tema.main_score() and tema.main_score().preview_url %}
+
+ {% endif %}
+ {% if tema.properties %}
+
+ {% for property in tema.properties %}
+
+ {{ property.value }}
+
+ {% endfor %}
+
+ {% endif %}
+
{% endfor %}
-
+
{% if prev_offset is not none or next_offset is not none %}
{% if prev_offset is not none %}
diff --git a/folkugat_web/assets/templates/lilypond/preview.ly b/folkugat_web/assets/templates/lilypond/preview.ly
new file mode 100644
index 0000000..34c4bc9
--- /dev/null
+++ b/folkugat_web/assets/templates/lilypond/preview.ly
@@ -0,0 +1,10 @@
+\version "2.24.4"
+{% if tune.score_source is not none %}
+\score {
+ \language "english"
+ <<
+{{ score_beginning }}
+{{ tune.score_source | safe }}
+ >>
+}
+{% endif %}
diff --git a/folkugat_web/dal/sql/temes/ddl.py b/folkugat_web/dal/sql/temes/ddl.py
index 68d409e..ddf4bf6 100644
--- a/folkugat_web/dal/sql/temes/ddl.py
+++ b/folkugat_web/dal/sql/temes/ddl.py
@@ -79,6 +79,7 @@ def create_scores_table(con: Connection):
errors TEXT NOT NULL,
img_url TEXT,
pdf_url TEXT,
+ preview_url TEXT,
hidden BOOLEAN,
FOREIGN KEY(tema_id) REFERENCES temes(id) ON DELETE CASCADE
)
diff --git a/folkugat_web/dal/sql/temes/scores.py b/folkugat_web/dal/sql/temes/scores.py
index b74418b..e50f165 100644
--- a/folkugat_web/dal/sql/temes/scores.py
+++ b/folkugat_web/dal/sql/temes/scores.py
@@ -6,7 +6,7 @@ from folkugat_web.dal.sql import Connection, get_connection
from folkugat_web.model import temes as model
from folkugat_web.model.lilypond.processing import RenderError
-ScoreRowTuple = tuple[int, int, str, str, str, str | None, str | None, bool]
+ScoreRowTuple = tuple[int, int, str, str, str, str | None, str | None, str | None, bool]
class ScoreRowDict(TypedDict):
@@ -17,6 +17,7 @@ class ScoreRowDict(TypedDict):
source: str
img_url: str | None
pdf_url: str | None
+ preview_url: str | None
hidden: bool
@@ -29,6 +30,7 @@ def score_to_row(score: model.Score) -> ScoreRowDict:
"source": score.source,
"img_url": score.img_url,
"pdf_url": score.pdf_url,
+ "preview_url": score.preview_url,
"hidden": score.hidden,
}
@@ -43,7 +45,8 @@ def row_to_score(row: ScoreRowTuple) -> model.Score:
errors=list(map(RenderError.from_dict, errors_dicts)),
img_url=row[5],
pdf_url=row[6],
- hidden=bool(row[7]),
+ preview_url=row[7],
+ hidden=bool(row[8]),
)
@@ -78,7 +81,7 @@ def get_scores(score_id: int | None = None, tema_id: int | None = None, con: Con
query = f"""
SELECT
- id, tema_id, title, source, errors, img_url, pdf_url, hidden
+ id, tema_id, title, source, errors, img_url, pdf_url, preview_url, hidden
FROM tema_scores
{filter_clause}
"""
@@ -92,9 +95,9 @@ def insert_score(score: model.Score, con: Connection | None = None) -> model.Sco
data = score_to_row(score)
query = f"""
INSERT INTO tema_scores
- (id, tema_id, title, source, errors, img_url, pdf_url, hidden)
+ (id, tema_id, title, source, errors, img_url, pdf_url, preview_url, hidden)
VALUES
- (:id, :tema_id, :title, :source, :errors, :img_url, :pdf_url, :hidden)
+ (:id, :tema_id, :title, :source, :errors, :img_url, :pdf_url, :preview_url, :hidden)
RETURNING *
"""
with get_connection(con) as con:
@@ -110,7 +113,7 @@ def update_score(score: model.Score, con: Connection | None = None):
UPDATE tema_scores
SET
tema_id = :tema_id, title = :title, source = :source, errors = :errors,
- img_url = :img_url, pdf_url = :pdf_url, hidden = :hidden
+ img_url = :img_url, pdf_url = :pdf_url, preview_url = :preview_url, hidden = :hidden
WHERE
id = :id
"""
diff --git a/folkugat_web/fragments/temes.py b/folkugat_web/fragments/temes.py
index 2283228..219ccc7 100644
--- a/folkugat_web/fragments/temes.py
+++ b/folkugat_web/fragments/temes.py
@@ -3,8 +3,7 @@ from folkugat_web.model import temes as model
from folkugat_web.model.lilypond.processing import RenderError
from folkugat_web.model.pagines import Pages
from folkugat_web.services import sessions as sessions_service
-from folkugat_web.services.temes import links as links_service
-from folkugat_web.services.temes import lyrics as lyrics_service
+from folkugat_web.services.temes import properties as properties_service
from folkugat_web.services.temes import query as temes_q
from folkugat_web.services.temes import scores as scores_service
from folkugat_web.services.temes import search as temes_s
@@ -43,8 +42,7 @@ def temes_busca(request: Request, logged_in: bool, query: str, offset: int = 0,
temes = (
FnChain.transform(temes) |
temes_q.temes_compute_stats |
- links_service.add_links_to_temes |
- lyrics_service.add_lyrics_to_temes |
+ properties_service.add_properties_to_temes |
scores_service.add_scores_to_temes |
list
).result()
diff --git a/folkugat_web/model/temes.py b/folkugat_web/model/temes.py
index 1b28dcf..d34af26 100644
--- a/folkugat_web/model/temes.py
+++ b/folkugat_web/model/temes.py
@@ -67,6 +67,7 @@ class Score:
source: str
img_url: str | None
pdf_url: str | None
+ preview_url: str | None
hidden: bool
@classmethod
@@ -81,6 +82,7 @@ class Score:
errors=[],
img_url=link.url if link.link_type is LinkType.IMAGE else None,
pdf_url=link.url if link.link_type is LinkType.PDF else None,
+ preview_url=None,
hidden=False,
)
diff --git a/folkugat_web/services/files.py b/folkugat_web/services/files.py
index 979388f..1efd34b 100644
--- a/folkugat_web/services/files.py
+++ b/folkugat_web/services/files.py
@@ -97,7 +97,8 @@ def get_orphan_files() -> Iterator[Path]:
link_urls = {link.url for link in links_dal.get_links()}
score_pdf_urls = {score.pdf_url for score in scores_dal.get_scores() if score.pdf_url is not None}
score_img_urls = {score.img_url for score in scores_dal.get_scores() if score.img_url is not None}
- alive_urls = link_urls | score_pdf_urls | score_img_urls
+ score_preview_urls = {score.preview_url for score in scores_dal.get_scores() if score.preview_url is not None}
+ alive_urls = link_urls | score_pdf_urls | score_img_urls | score_preview_urls
return filter(
lambda p: p.is_file() and get_db_file_path(p) not in alive_urls,
itertools.chain(
diff --git a/folkugat_web/services/lilypond/render.py b/folkugat_web/services/lilypond/render.py
index 72e8e4e..b976f11 100644
--- a/folkugat_web/services/lilypond/render.py
+++ b/folkugat_web/services/lilypond/render.py
@@ -18,7 +18,7 @@ RenderResult = Result[Path, list[RenderError]]
class RenderOutput(enum.Enum):
PDF = "pdf"
PNG_CROPPED = "png-cropped"
- PNG_PREVIEW = "png-preview"
+ PREVIEW = "preview"
async def render(
@@ -61,16 +61,16 @@ async def render_file(
]
output_file = output_file.with_suffix(".cropped.png")
# output_file = output_file.with_suffix(".png")
- case RenderOutput.PNG_PREVIEW:
+ case RenderOutput.PREVIEW:
command = [
"lilypond",
- "-f", "png",
+ "-f", "svg",
"-dpreview=#t",
"-dno-print-pages",
"-o", str(output_file),
str(input_file)
]
- output_file = output_file.with_suffix(".preview.png")
+ output_file = output_file.with_suffix(".preview.svg")
proc = await asyncio.create_subprocess_exec(
*command,
diff --git a/folkugat_web/services/lilypond/source.py b/folkugat_web/services/lilypond/source.py
index de7f98e..46397a3 100644
--- a/folkugat_web/services/lilypond/source.py
+++ b/folkugat_web/services/lilypond/source.py
@@ -11,6 +11,13 @@ def tune_source(tune: LilypondTune) -> str:
)
+def preview_source(tune: LilypondTune) -> str:
+ return templates.get_template("lilypond/preview.ly").render(
+ score_beginning=SCORE_BEGINNING,
+ tune=tune,
+ )
+
+
def set_source(tune_set: LilypondSet) -> str:
return templates.get_template("lilypond/tune_set.ly").render(
score_beginning=SCORE_BEGINNING,
diff --git a/folkugat_web/services/temes/scores.py b/folkugat_web/services/temes/scores.py
index fd5d56f..b603dce 100644
--- a/folkugat_web/services/temes/scores.py
+++ b/folkugat_web/services/temes/scores.py
@@ -1,14 +1,12 @@
+import dataclasses
from collections.abc import Iterable, Iterator
-from fastapi import HTTPException
from folkugat_web.dal.sql.temes import scores as scores_dal
from folkugat_web.model import temes as model
-from folkugat_web.model.lilypond import score as lilypond_model
-from folkugat_web.services import lilypond
-from folkugat_web.services.temes import lyrics as lyrics_service
-from folkugat_web.services.temes import properties as properties_service
-from folkugat_web.services.temes import query as temes_q
-from folkugat_web.utils import FnChain
+from folkugat_web.services import files as files_service
+from folkugat_web.services.lilypond import build as lilypond_build
+from folkugat_web.services.lilypond import render as lilypond_render
+from folkugat_web.services.lilypond import source as lilypond_source
def add_scores_to_tema(tema: model.Tema) -> model.Tema:
@@ -42,34 +40,56 @@ def create_score(tema_id: int) -> model.Score:
errors=[],
img_url=None,
pdf_url=None,
+ preview_url=None,
hidden=True,
)
return scores_dal.insert_score(score=new_score)
-def build_tune_set_full_source(tema_ids: list[int]) -> str:
- temes = (
- FnChain.transform(temes_q.get_temes_by_ids(tema_ids=tema_ids)) |
- properties_service.add_properties_to_temes |
- lyrics_service.add_lyrics_to_temes |
- add_scores_to_temes |
- list
- ).result()
- if not temes:
- return ""
- set_title = " i ".join(filter(bool, [
- ", ".join([tema.title for tema in temes[:-1]]),
- temes[-1].title
- ]))
- tune_set = lilypond_model.LilypondSet(
- title=set_title,
- tunes=[
- lilypond_model.LilypondTune(
- header=lilypond_model.HeaderData.from_tema(tema=tema),
- score_source=tema.scores[0].source if tema.scores else None,
- lyrics=lilypond_model.LyricsText.from_lyrics(lyrics=tema.lyrics[0]) if tema.lyrics else None,
- ) for tema in temes
- ]
+async def render_score(score: model.Score, title: str, source: str) -> model.Score:
+ tune = lilypond_build.tune_from_tema_id(tema_id=score.tema_id, score_source=source)
+ output_file = files_service.create_tema_filename(tema_id=score.tema_id)
+
+ tune_source = lilypond_source.tune_source(tune=tune)
+ async with files_service.tmp_file(content=tune_source) as source_file:
+ # Render PDF
+ pdf_result = await lilypond_render.render_file(
+ input_file=source_file,
+ output=lilypond_render.RenderOutput.PDF,
+ output_file=output_file,
+ )
+ if errors := pdf_result.error:
+ return dataclasses.replace(score, source=source, title=title, errors=errors)
+ # Render IMAGE
+ img_result = await lilypond_render.render_file(
+ input_file=source_file,
+ output=lilypond_render.RenderOutput.PNG_CROPPED,
+ output_file=output_file,
+ )
+ if errors := img_result.error:
+ return dataclasses.replace(score, source=source, title=title, errors=errors)
+
+ preview_source = lilypond_source.preview_source(tune=tune)
+ async with files_service.tmp_file(content=preview_source) as source_file:
+ # Render PREVIEW
+ preview_result = await lilypond_render.render_file(
+ input_file=source_file,
+ output=lilypond_render.RenderOutput.PREVIEW,
+ output_file=output_file,
+ )
+ if errors := preview_result.error:
+ return dataclasses.replace(score, source=source, title=title, errors=errors)
+
+ pdf_file = pdf_result.result
+ img_file = img_result.result
+ preview_file = preview_result.result if preview_result.result and preview_result.result.exists() else None
+
+ return dataclasses.replace(
+ score,
+ source=source,
+ title=title,
+ pdf_url=files_service.get_db_file_path(pdf_file) if pdf_file else None,
+ img_url=files_service.get_db_file_path(img_file) if img_file else None,
+ preview_url=files_service.get_db_file_path(preview_file) if preview_file else None,
+ errors=[],
)
- print("TUNE SET HASH: ", tune_set.hash().hex())
- return lilypond.tune_set_score(tune_set=tune_set)
diff --git a/scripts/03_add_preview_url.py b/scripts/03_add_preview_url.py
new file mode 100644
index 0000000..e146c17
--- /dev/null
+++ b/scripts/03_add_preview_url.py
@@ -0,0 +1,8 @@
+from folkugat_web.dal.sql import get_connection
+
+with get_connection() as con:
+ cur = con.cursor()
+ alter_query = """ ALTER TABLE tema_scores ADD COLUMN preview_url TEXT"""
+ _ = cur.execute(alter_query)
+
+print("DONE!")