Browse MixtapesΒΆ
The browse_mixtapes Flask blueprint (routes/browse_mixtapes.py) powers the Mixtapes listing page with advanced search and sorting capabilities, file serving, and redirection to the public player. It explains the routes, authentication flow, interaction with MixtapeManager, and the front-end assets (browse_mixtapes.html, CSS, and JavaScript).
π High-Level OverviewΒΆ
| Component | Responsibility |
|---|---|
browse_mixtapes Blueprint (routes/browse_mixtapes.py) |
Registers all UI-facing routes under the /mixtapes prefix, enforces authentication, handles search/sort parameters, and delegates data access to MixtapeManager. |
MixtapeManager (mixtape_manager.py) |
Reads/writes mixtape JSON files, manages cover images, and provides list_all() for the browse view. |
Templates (templates/browse_mixtapes.html) |
Renders the list of mixtapes with a compact search/sort bar and mixtape cards, each with cover, meta info, and action buttons (edit, play, share, delete). |
Static assets (static/css/browse_mixtapes.css, static/js/browser/*.js) |
Provide responsive styling, hybrid search functionality (instant title + deep track search), sorting logic, and delete-confirmation modal. |
Authentication (auth.py) |
@require_auth decorator and check_auth() helper ensure only logged-in users can reach any route in this blueprint. |
πΊοΈ Flask Blueprint & RoutesΒΆ
| HTTP Method | URL Pattern | View Function | Query Parameters | Description |
|---|---|---|---|---|
GET |
/mixtapes/ |
browse() |
sort_by, sort_order, search, deep |
Retrieves all mixtapes (MixtapeManager.list_all()), applies search/sort filters, and renders browse_mixtapes.html. |
GET |
/mixtapes/play/<slug> |
play(slug) |
- | Redirect to the public player (play.public_play) for the given mixtape slug. |
GET |
/mixtapes/files/<path:filename> |
files(filename) |
- | Serves static files (JSON, cover images, etc.) from the configured MIXTAPE_DIR. |
POST |
/mixtapes/delete/<slug> |
delete_mixtape(slug) |
- | Deletes the mixtape JSON and its cover image; returns JSON { success: true } or an error. |
before_request |
β | blueprint_require_auth() |
- | Runs before every request in this blueprint; redirects unauthenticated users to the landing page (url_for("landing")). |
All routes are wrapped with @require_auth (except the before_request hook, which performs the same check).
Query ParametersΒΆ
Sort Parameters:
sort_by: Field to sort by (updated_at,created_at,title,track_count)sort_order: Sort direction (asc,desc)- Default:
sort_by=updated_at&sort_order=desc(most recently modified first)
Search Parameters:
search: Search query stringdeep: Boolean (true/false) indicating deep search modefalseor absent: Client-side title filtering onlytrue: Server-side search through tracks, artists, albums, and liner notes
Examples:
/mixtapes/?sort_by=title&sort_order=asc
/mixtapes/?search=rock&deep=true&sort_by=track_count&sort_order=desc
π Authentication & Access ControlΒΆ
- Decorator β
@require_auth(imported fromauth.py) checks the session for a valid user. If the check fails, the decorator returns a redirect to the login page. - Blueprint-wide guard β
@browser.before_requestexecutescheck_auth()for every request hitting this blueprint. This is a defensive second line; even if a route is accidentally left undecorated, the guard will still enforce authentication.
Result: Only logged-in users can view the mixtape list, play a mixtape, download files, or delete a mixtape.
π Search & Sort FeaturesΒΆ
Hybrid Search SystemΒΆ
The browse page implements a two-tier search system:
-
Instant Title Search (Client-Side)
- Filters mixtapes by title as you type
- No server round-trip required
- Instant visual feedback
- Implemented in
static/js/browser/search.js
-
Deep Search (Server-Side)
- Click "Tracks" button to search within:
- Track names (song titles)
- Artist names
- Album names
- Mixtape titles
- Liner notes
- Implemented in
_deep_search_mixtapes()helper function - Returns filtered list to template
Sorting SystemΒΆ
Users can sort by 8 different combinations via a single dropdown:
Date Options:
- π Recent First (updated_at desc)
- π Oldest First (updated_at asc)
- π Newest Created (created_at desc)
- π Oldest Created (created_at asc)
Name & Size Options:
- π€ A β Z (title asc)
- π€ Z β A (title desc)
- π΅ Most Tracks (track_count desc)
- π΅ Fewest Tracks (track_count asc)
Sorting is applied after search filtering, so search results respect the selected sort order.
π Data Flow & Server-Side LogicΒΆ
Listing Mixtapes (GET /mixtapes/)ΒΆ
- Request β Flask routes the request to
browse()(protected). - Parse parameters β Extract
sort_by,sort_order,search, anddeepfrom query string. - Mixtape retrieval β
mixtape_manager.list_all()reads every*.jsonfile inapp.config["MIXTAPE_DIR"]and returns a list of dicts. -
Apply search β If
searchanddeep=true, filter mixtapes using_deep_search_mixtapes():def _deep_search_mixtapes(mixtapes: list[dict], query: str) -> list[dict]: """Searches across mixtape metadata and all tracks""" query_lower = query.lower() results = [] for mixtape in mixtapes: # Check title, liner notes if query_lower in mixtape.get('title', '').lower(): results.append(mixtape) continue # Check all tracks (name, artist, album) for track in mixtape.get('tracks', []): if (query_lower in track.get('track', '').lower() or query_lower in track.get('artist', '').lower() or query_lower in track.get('album', '').lower()): results.append(mixtape) break return results -
Apply sorting β Sort the filtered list based on
sort_byandsort_order: -
Template rendering β Pass
mixtapes,sort_by,sort_order,search_query, andsearch_deepto template. - HTML output β Renders search/sort controls and filtered mixtape cards.
Playing a Mixtape (GET /mixtapes/play/<slug>)ΒΆ
-
The view simply redirects to the public player route defined elsewhere (e.g. play.public_play).
-
The client ends up on
/play/<slug>#play, where the full mixtape UI is rendered.
Serving Files (GET /mixtapes/files/<filename>)ΒΆ
- Uses Flask's
send_from_directoryto serve any file underapp.config["MIXTAPE_DIR"]. - This includes the JSON file (
<slug>.json) and cover images (covers/<slug>.jpg).
Deleting a Mixtape (POST /mixtapes/delete/<slug>)ΒΆ
- Existence check β Verifies that
<slug>.jsonexists; if not, returns404with JSON error. - Calls
mixtape_manager.delete(slug), which removes the JSON file and any associated cover image (covers/<slug>.jpg). - Returns JSON
{ "success": true }on success, or{ "success": false, "error": "..." }with the appropriate HTTP status on failure.
Error HandlingΒΆ
- All routes catch generic
Exceptionand log the traceback via the injectedlogger. - Errors are reported to the client as JSON with a descriptive
errorfield and an appropriate HTTP status code (400,404,500).
π₯οΈ UI Layout (Jinja Template β browse_mixtapes.html)ΒΆ
| Section | Details |
|---|---|
| Header | Compact header with "My Mixtapes" title and a "New" button linking to /editor. Responsive sizing (smaller on mobile). |
| Search & Sort Bar | Compact card containing: β’ Search input with clear button β’ "Tracks" button (with tooltip) for deep search β’ Combined sort dropdown (field + order in one) β’ Minimal vertical space (~50px desktop, ~70px mobile) β’ Bootstrap theme-aware styling |
| Search Result Banner | When search is active, shows info banner with query, result count, and "Clear Search" button. Distinguishes between title search and deep search results. |
| Mixtape Cards | Loop over filtered/sorted mixtapes. Each card (.mixtape-item) contains: β’ Cover (.mixtape-cover) β’ Title (.mixtape-title) β’ Meta info (.mixtape-meta) β’ Action buttons (.action-btn): edit, play, QR share, delete |
| Empty State | Context-aware: β’ No mixtapes: "No mixtapes yet..." β’ No search results: "No mixtapes found" with clear search button |
| Delete Confirmation Modal | Modal (#deleteConfirmModal) that asks the user to confirm deletion; populated with the mixtape title via JS. |
| Delete Success Toast | Toast (#deleteSuccessToast) shown after a successful deletion. |
| JS Entry Point | <script type="module" src="{{ url_for('static', filename='js/browser/index.js') }}"></script> β wires up search, sort, delete, QR share, and tooltip functionality. |
All UI elements use Bootstrap 5 utilities and custom CSS variables (--bs-body-bg, --bs-border-color, --bs-body-color) to stay theme-aware (light/dark modes).
Compact Search/Sort Bar FeaturesΒΆ
Space Efficiency:
- 60%+ vertical space reduction compared to separate cards
- Single card with all controls
- Mobile-optimized with reduced padding
Responsive Layout:
- Desktop (β₯992px): Search input expands to fill space, Tracks + Sort align right
- Tablet (768-991px): Two-row layout with natural wrapping
- Mobile (<768px): Stacked controls, full-width inputs
- Tiny (<576px): Extra compact sizing
Theming:
- Search icon respects Bootstrap color variables
- No hardcoded colors (works in light/dark mode)
- Proper contrast in all themes
Tooltips:
- Tracks button has tooltip: "Search within song names, artists, and albums"
- Works on both enabled and disabled states
- Helps users understand deep search feature
π§± Static Assets (CSS & JS)ΒΆ
browse_mixtapes.cssΒΆ
Search & Sort Styling:
- Compact card with minimal padding (
p-2on mobile,p-md-3on desktop) - Theme-aware colors using CSS variables
- Input group styling with seamless borders
- Combined sort dropdown with emoji icons
- Responsive adjustments for all screen sizes
Mixtape Cards:
- Responsive card layout β Flexbox with wrapping, subtle shadows, and a hover lift effect
- Action buttons β Circular, color-coded (edit=primary, play=success, share=info, delete=danger). Hover scales the button
- Mobile adjustments β Smaller cover size, reduced button dimensions, and a stacked layout for very narrow viewports (
max-width: 480px)
Animations:
- Slide-down animation for search result banner
- Smooth hover transitions on cards and buttons
- Subtle lift effect on card hover
JavaScript ModulesΒΆ
| File | Exported function(s) | Purpose |
|---|---|---|
search.js |
initSearch() |
Handles hybrid search: instant title filtering (client-side), deep search button click, clear search, and Enter key support. Disables client-side filtering when viewing deep search results. |
sorting.js |
initSorting() |
Handles combined sort dropdown changes, updates URL with sort parameters, preserves search state during sort changes. |
deleteMixtape.js |
initDeleteMixtape() |
Handles the delete workflow: opens the confirmation modal, sends a POST /mixtapes/delete/<slug> request, shows success toast, and reloads the page. |
index.js |
β | Imports and initializes all modules on DOMContentLoaded: search, sorting, delete, QR share, and Bootstrap tooltips. |
Common Module (Shared):
../common/qrShare.js- Provides QR code generation, modal display, copy-to-clipboard, and download functionality. Used across browser, editor, and player pages.
All scripts are ES6 modules (type="module"), ensuring they are loaded after the DOM is ready and that they don't pollute the global namespace.
Search Implementation DetailsΒΆ
Client-Side Title Search:
function filterByTitle(query) {
// Skip if on deep search results page
if (isDeepSearchActive) return;
mixtapeItems.forEach(item => {
const title = item.querySelector('.mixtape-title').textContent;
if (title.toLowerCase().includes(query.toLowerCase())) {
item.style.display = originalDisplays.get(item);
} else {
item.style.display = 'none';
}
});
}
Deep Search Navigation:
deepSearchBtn.addEventListener('click', () => {
const url = new URL(window.location.href);
url.searchParams.set('search', query);
url.searchParams.set('deep', 'true');
// Preserve sort parameters
window.location.href = url.toString();
});
Key Features:
- Instant filtering respects search state (doesn't interfere with server-side results)
- Clear button removes both client-side filters and navigates away from deep search
- Enter key triggers deep search
- URL parameters maintain complete state (search + sort)
π Class & Sequence DiagramsΒΆ
Class DiagramΒΆ
classDiagram
class browse_mixtapes_Blueprint {
+browse()
+play(slug)
+files(filename)
+delete_mixtape(slug)
+blueprint_require_auth()
-_deep_search_mixtapes()
}
class MixtapeManager {
+list_all()
+delete(slug)
}
class AuthSystem {
+require_auth()
+check_auth()
}
class SearchSystem {
+initSearch()
+filterByTitle()
+deepSearch()
}
class SortSystem {
+initSorting()
+updateURL()
}
browse_mixtapes_Blueprint --> MixtapeManager : uses
browse_mixtapes_Blueprint --> AuthSystem : enforces authentication
browse_mixtapes_Blueprint --> SearchSystem : client-side filtering
browse_mixtapes_Blueprint --> SortSystem : URL parameter management
Sequence Diagram - Searching and Sorting MixtapesΒΆ
sequenceDiagram
participant User
participant BrowserJS
participant FlaskApp
participant Blueprint as browse_mixtapes
participant MixtapeManager
User->>BrowserJS: Types in search box
BrowserJS->>BrowserJS: Filter by title (instant)
User->>BrowserJS: Clicks "Tracks" button
BrowserJS->>FlaskApp: GET /mixtapes/?search=rock&deep=true&sort_by=title&sort_order=asc
FlaskApp->>Blueprint: browse(search='rock', deep='true', ...)
Blueprint->>MixtapeManager: list_all()
MixtapeManager-->>Blueprint: all mixtapes
Blueprint->>Blueprint: _deep_search_mixtapes(mixtapes, 'rock')
Blueprint->>Blueprint: sort by title ascending
Blueprint-->>FlaskApp: render template with filtered results
FlaskApp-->>User: HTML page with search results
Sequence Diagram - Deleting a MixtapeΒΆ
sequenceDiagram
participant User
participant BrowserJS
participant FlaskApp
participant browse_mixtapes as Blueprint
participant MixtapeManager
User->>BrowserJS: Click Delete β opens modal
BrowserJS->>FlaskApp: POST /mixtapes/delete/<slug>
FlaskApp->>Blueprint: delete_mixtape(slug)
Blueprint->>MixtapeManager: delete(slug)
MixtapeManager-->>Blueprint: success
Blueprint-->>FlaskApp: JSON {success:true}
FlaskApp-->>BrowserJS: JSON response
BrowserJS->>BrowserJS: Show success toast, reload page
π― User Experience FeaturesΒΆ
Search FlowΒΆ
- Quick Title Search:
- User types in search box
- Results filter instantly (no page reload)
- Clear button appears
-
Tracks button becomes enabled
-
Deep Search:
- User clicks "Tracks" button (or presses Enter)
- Page reloads with
?search=query&deep=true - Server searches all metadata
- Results include mixtapes with matching songs/artists/albums
-
Banner shows "Deep search results for: {query}"
-
Clear Search:
- Click clear button (X)
- Returns to full mixtape list
- Preserves sort settings
Sort FlowΒΆ
- Select Sort Option:
- User selects from dropdown (e.g., "π΅ Most Tracks")
- Page reloads with new sort parameters
-
Search state is preserved if active
-
Combined with Search:
- Search results can be re-sorted
- Sort order maintained during searches
- URL reflects complete state
Mobile OptimizationsΒΆ
- Compact Controls: 60% less vertical space than desktop
- Touch-Friendly: All buttons meet WCAG size standards
- Smart Layout: Inputs stack appropriately
- Visual Clarity: Emoji icons aid recognition
- Performance: Client-side filtering remains instant
π APIΒΆ
browse_mixtapes
ΒΆ
Functions:
| Name | Description |
|---|---|
create_browser_blueprint |
Creates and configures the Flask blueprint for browsing, playing, and managing mixtapes. |
create_browser_blueprint(mixtape_manager, func_processing_status, logger=None)
ΒΆ
Creates and configures the Flask blueprint for browsing, playing, and managing mixtapes.
Sets up routes for listing mixtapes, serving cover images and files, deleting mixtapes, and handling authentication for all routes in the blueprint.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mixtape_manager
|
MixtapeManager
|
The manager instance for retrieving and managing mixtapes. |
required |
func_processing_status
|
any
|
A function to check the current indexing or processing status. |
required |
logger
|
Logger
|
The logger instance for error reporting. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Blueprint |
Blueprint
|
The configured Flask blueprint for browsing and managing mixtapes. |
Source code in src/routes/browse_mixtapes.py
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | |
π§ ConfigurationΒΆ
The browse functionality relies on the following configuration values:
MIXTAPE_DIR: Directory where mixtape JSON files and covers are storedDATA_ROOT: Root directory for application data- Session configuration for authentication
π¨ CustomizationΒΆ
Adding New Sort OptionsΒΆ
- Add option to HTML template select dropdown
- Update
browse()function to handle new sort field - Ensure proper sorting logic (ascending/descending)
Modifying Search BehaviorΒΆ
Client-side (Title Search):
- Edit
static/js/browser/search.js - Modify
filterByTitle()function
Server-side (Deep Search):
- Edit
_deep_search_mixtapes()inroutes/browse_mixtapes.py - Add or modify search fields
ThemingΒΆ
All colors use CSS custom properties:
Override these in your theme CSS to customize appearance.
π± Responsive BreakpointsΒΆ
| Breakpoint | Width | Layout Changes |
|---|---|---|
| Desktop | β₯992px | Search expands, controls align right, single row |
| Tablet | 768-991px | Two-row layout, natural wrapping |
| Mobile | 576-767px | Stacked pairs, larger touch targets |
| Tiny | <576px | Full stack, extra compact sizing |
