Skip to content

Commit a4f20f7

Browse files
authored
Merge pull request #80 from guerda/feat_cache
feat: add cache to all application paths
2 parents c485967 + bd8ee73 commit a4f20f7

4 files changed

Lines changed: 65 additions & 13 deletions

File tree

apitool.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from fastapi import Response
2+
from fastapi.staticfiles import StaticFiles
3+
4+
5+
class StaticFilesCache(StaticFiles):
6+
def __init__(self, *args, cachecontrol: str, **kwargs):
7+
self.cachecontrol = cachecontrol
8+
super().__init__(*args, **kwargs)
9+
10+
def file_response(self, *args, **kwargs) -> Response:
11+
resp: Response = super().file_response(*args, **kwargs)
12+
resp.headers.setdefault("Cache-Control", self.cachecontrol)
13+
return resp

app.py

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from logfmter import Logfmter
1111
from pydantic_settings import BaseSettings
1212

13+
from apitool import StaticFilesCache
1314
from beetsstatistics import AlbumSort, BeetsStatistics, DBNotFoundError, DBQueryError
1415

1516
log_format = "%(asctime)s [%(levelname)-7s] [%(name)-12s] %(name)s - %(message)s"
@@ -51,7 +52,10 @@ async def get_beets_statistics():
5152

5253
settings = Settings()
5354
app = FastAPI()
54-
app.mount("/static", StaticFiles(directory="static"), name="static")
55+
static_files_with_cache = StaticFilesCache(
56+
directory="static", cachecontrol="public, max-age: 86400"
57+
)
58+
app.mount("/static", static_files_with_cache, name="static")
5559

5660
templates = Jinja2Templates(directory="templates")
5761
templates.env.filters["quote_plus"] = lambda u: quote_plus(u)
@@ -83,7 +87,7 @@ async def get_general_stats(
8387
raise HTTPException(
8488
status_code=500, detail="Could not query general statistics: {}".format(e)
8589
)
86-
return templates.TemplateResponse(
90+
response = templates.TemplateResponse(
8791
request=request,
8892
name="index.html",
8993
context={
@@ -99,6 +103,8 @@ async def get_general_stats(
99103
"recently_added_albums": recently_added_albums,
100104
},
101105
)
106+
_inject_cache_headers(response.headers)
107+
return response
102108

103109

104110
@app.get("/albums", response_class=HTMLResponse)
@@ -108,9 +114,11 @@ async def get_album_stats(
108114
sort_by: AlbumSort = AlbumSort.ARTIST,
109115
):
110116
albums = beets_statistics.get_albums_from_db(sort_by=sort_by)
111-
return templates.TemplateResponse(
117+
response = templates.TemplateResponse(
112118
request=request, name="albums.html", context={"albums": albums}
113119
)
120+
_inject_cache_headers(response.headers)
121+
return response
114122

115123

116124
@app.get("/genres", response_class=HTMLResponse)
@@ -124,11 +132,13 @@ async def get_genre_count(
124132
for genre in genres[0]:
125133
genre_list.append(genre["genre"])
126134
count_list.append(genre["count"])
127-
return templates.TemplateResponse(
135+
response = templates.TemplateResponse(
128136
request=request,
129137
name="genres.html",
130138
context={"genres": genres, "genre_list": genre_list, "count_list": count_list},
131139
)
140+
_inject_cache_headers(response.headers)
141+
return response
132142

133143

134144
@app.get("/artists", response_class=HTMLResponse)
@@ -143,7 +153,7 @@ async def get_artist_stats(
143153
artist_list.append(artist["artist"])
144154
count_list.append(artist["track_count"])
145155
count = beets_statistics.get_track_count()
146-
return templates.TemplateResponse(
156+
response = templates.TemplateResponse(
147157
request=request,
148158
name="artists.html",
149159
context={
@@ -153,6 +163,8 @@ async def get_artist_stats(
153163
"count_list": count_list,
154164
},
155165
)
166+
_inject_cache_headers(response.headers)
167+
return response
156168

157169

158170
@app.get("/decades", response_class=HTMLResponse)
@@ -161,11 +173,13 @@ async def get_track_decades(
161173
beets_statistics: Annotated[BeetsStatistics, Depends(get_beets_statistics)],
162174
):
163175
decades = beets_statistics.get_track_decades()
164-
return templates.TemplateResponse(
176+
response = templates.TemplateResponse(
165177
request=request,
166178
name="decades.html",
167179
context={"decades": decades},
168180
)
181+
_inject_cache_headers(response.headers)
182+
return response
169183

170184

171185
@app.get("/quality", response_class=HTMLResponse)
@@ -175,11 +189,14 @@ async def get_track_quality(
175189
):
176190
bitrates = beets_statistics.get_track_quality()
177191
worst_quality_tracks = beets_statistics.get_worst_quality_tracks()
178-
return templates.TemplateResponse(
192+
193+
response = templates.TemplateResponse(
179194
request=request,
180195
name="quality.html",
181196
context={"bitrates": bitrates, "worst_quality_tracks": worst_quality_tracks},
182197
)
198+
_inject_cache_headers(response.headers)
199+
return response
183200

184201

185202
@app.get("/genre-decade-heatmap", response_class=HTMLResponse)
@@ -222,7 +239,7 @@ async def get_genre_decade_heatmap(
222239
sorted_genre = dict(sorted(heatmap[genre].items()))
223240
heatmap[genre] = sorted_genre
224241

225-
return templates.TemplateResponse(
242+
response = templates.TemplateResponse(
226243
request=request,
227244
name="genre-decade-heatmap.html",
228245
context={
@@ -231,6 +248,8 @@ async def get_genre_decade_heatmap(
231248
"genre_list": genre_list,
232249
},
233250
)
251+
_inject_cache_headers(response.headers)
252+
return response
234253

235254

236255
@app.get("/cover/{album_id}", response_class=FileResponse)
@@ -241,7 +260,21 @@ async def get_album_cover(
241260
album_cover_path = beets_statistics.get_album_cover_path(album_id)
242261
if album_cover_path is None:
243262
album_cover_path = "static/blank.png"
244-
return album_cover_path
263+
264+
response = FileResponse(album_cover_path)
265+
_inject_cache_headers_for_images(response.headers)
266+
return response
267+
268+
269+
def _inject_cache_headers_for_images(headers):
270+
headers["Cache-Control"] = "public, max-age: 86400"
271+
headers["Vary"] = "Accept-Encoding"
272+
273+
274+
def _inject_cache_headers(headers):
275+
headers["Cache-Control"] = (
276+
"public, s-maxage=30, stale-while-revalidate=30, stale-if-error=300"
277+
)
245278

246279

247280
@app.get("/added-timeline", response_class=HTMLResponse)
@@ -250,11 +283,13 @@ async def get_added_timeline(
250283
beets_statistics: Annotated[BeetsStatistics, Depends(get_beets_statistics)],
251284
):
252285
timeline = beets_statistics.get_added_timeline()
253-
return templates.TemplateResponse(
286+
response = templates.TemplateResponse(
254287
request=request,
255288
name="added-timeline.html",
256289
context={"timeline": timeline},
257290
)
291+
_inject_cache_headers(response.headers)
292+
return response
258293

259294

260295
@app.get("/duplicates", response_class=HTMLResponse)
@@ -264,9 +299,11 @@ async def get_duplicates(
264299
):
265300
duplicates = beets_statistics.get_duplicates()
266301

267-
return templates.TemplateResponse(
302+
response = templates.TemplateResponse(
268303
request=request, name="duplicates.html", context={"duplicates": duplicates}
269304
)
305+
_inject_cache_headers(response.headers)
306+
return response
270307

271308

272309
@app.get("/not-in-mb", response_class=HTMLResponse)
@@ -277,8 +314,10 @@ async def get_not_in_mb(
277314
items_not_in_mb = beets_statistics.get_items_not_in_mb()
278315
albums_not_in_mb = beets_statistics.get_albums_not_in_mb()
279316

280-
return templates.TemplateResponse(
317+
response = templates.TemplateResponse(
281318
request=request,
282319
name="not-in-mb.html",
283320
context={"tracks": items_not_in_mb, "albums": albums_not_in_mb},
284321
)
322+
_inject_cache_headers(response.headers)
323+
return response

templates/footer.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
<footer><div id="footer"><a href="https://github.com/guerda/beets-statistics">beets-statistics on GithHub</a></div></footer>
2-
<script src="static/plotly.min.js"></script>
2+
<script src="static/plotly-3.1.0.min.js"></script>

0 commit comments

Comments
 (0)