A personal music review and listing project with yearly and all-time rankings.
The project is split into:
- a static front-end (GitHub Pages)
- a Python API (Render)
Opearatic/
│
├── api/ # Python API
│ ├── json/ # Pre-generated data (years + all-time pages)
│ ├── src/
│ │ ├── app.py # FastAPI application
│ │ ├── analyzer.py # Data processing logic
│ │ └── cache.py # Cache generation script
│ │ └── config.py # Path identifier for deployment environment
│ └── requirements.txt
│
├── www/ # Front-end
│ ├── html/
│ ├── css/
│ ├── js/
│ └── img/
│
└── README.md
This project uses a static architecture:
- All heavy processing is done offline or on deploy
- The API only reads JSON files
- Pagination is handled by pre-splitting data into pages
This allows:
- low response times
- minimal server costs
- simple deployment
- automation using GitHub Actions
- Built with FastAPI library
- Deployed on Render
- Serves data from pre-generated JSON files
- GET /years
- GET /pages
- GET /year/{year}
- GET /all-time?page={pageNumber}
All endpoints return already-ordered data.
The cache is generated by:
cd api
python -m src.cache.py
What it does:
- Generates the all-time ranking using a point system
- Sorts artists by points
- Splits results into pages (50 items per page)
- Saves them as:
api/json/all-time-{pageNumber}.json
This avoids runtime processing overload.
A GitHub Action automatically:
- Runs the cache generation script if there are changes in the source material or in the code
- Commits new JSON files only if they changed
This:
- guarantees list consistency
- only commit when necessary
- avoids redundant deployments
- Pure HTML, CSS and JavaScript
- Hosted on GitHub Pages
- Consumes the API via
fetch
The frontend:
- handles the pagination system of the API
- switches pages via query params
- stays completely static
-
www/index.htmlredirects to the html folder (needed because of GitHub pages structure). - All HTML files have a div with
navbar-navclass that has the upper navigation bar and a footer with aditional social media linking. - Decade-end lists, All-Time, or Genre-based lists could be added as new dropdown lists in the navbar, being logically separated from the Year-End lists. These lists would be in
api/jsonand would be added inanalyzer.pyas name exceptions inget_available_years(). New functions to get all decades, or all genres could be added as well to support those new features. -
www/html/years.htmlis the true home. It has an about section and a list with all available Year-End lists. -
www/html/year-end_list.htmlrenders dynamically the year-end lists based on query parameters that define the year (?year=2022) and the API result. -
www/html/all-time.htmlrenders dynamically 50 of the best artists in the ranking, based on which page is being loaded. The page is defined using query parameters as well (?page=2), and shows the artists from rank$1 + (page-1) * 50$ to rank$page * 50$ .
The API only allows requests from:
https://peramoniss.github.io
This prevents unauthorized usage while keeping the frontend functional. For that to work, the Render environment has the variable ENV: PROD. Changing to DEV allows requests from localhosts.
- GitHub Pages
- Source:
/wwwfolder
- Render (Free Tier)
- Auto-deploy on new commits
- Hosted in Virginia (USA)
- Start command is:
cd api
uvicorn src.app:app --host 0.0.0.0 --port 10000
The API may take a around 50 seconds to wake up after inactivity.
*You can run locally for testing using:
cd api
uvicorn src.app:app --reload
And then changing every reference to https://opearatic.onrender.com on the www/js/ files to http://127.0.0.1:8000, keeping the subdomains intact.
- No database is used
- No user-generated content
- No runtime writes to disk
- Everything is versioned and reproducible
Personal project.