Skip to main content

max / makenotwork

7.0 KB · 173 lines History Blame Raw
1 {% extends "base.html" %}
2
3 {% block title %}{{ item.title }} - Library - Makenotwork{% endblock %}
4
5 {% block head %}
6 <meta name="robots" content="noindex">
7 <link rel="stylesheet" href="/static/media-player.css">
8 {% endblock %}
9
10 {% block content %}
11 {% include "partials/site_header.html" %}
12
13 <article class="media-container">
14 <p class="library-back">
15 <a href="/i/{{ item.id }}">&larr; Store page</a> &middot;
16 <a href="/library">Your library</a>
17 </p>
18
19 <header class="author-header">
20 <div class="author-avatar">{{ creator_avatar_initials }}</div>
21 <div class="author-info">
22 <div class="author-name"><a href="/u/{{ creator_username }}">{% if let Some(name) = creator_display_name %}{{ name }}{% else %}{{ creator_username }}{% endif %}</a></div>
23 <div class="media-meta">{{ item.release_date }}{% match item.content %}{% when crate::types::ItemContent::Audio with { duration, .. } %}{% if let Some(dur) = duration %} | {{ dur }}{% endif %}{% when _ %}{% endmatch %}</div>
24 </div>
25 </header>
26
27 <h1 class="media-title">{{ item.title }}</h1>
28
29 {% if let Some(project_title) = project_title %}
30 <p class="media-series">
31 <a href="/p/{{ project_slug }}">{{ project_title }}</a>{% match item.content %}{% when crate::types::ItemContent::Audio with { episode_number, .. } %}{% if let Some(episode) = episode_number %} | Episode {{ episode }}{% endif %}{% when _ %}{% endmatch %}
32 </p>
33 {% endif %}
34
35 <div class="cover-art">
36 {% match item.content %}
37 {% when crate::types::ItemContent::Audio with { cover_url, .. } %}
38 {% if let Some(url) = cover_url %}
39 <img src="{{ url }}" alt="{{ item.title }} cover art">
40 {% endif %}
41 {% when _ %}
42 {% endmatch %}
43 </div>
44
45 <div class="media-player">
46 <audio id="media-a" preload="metadata">
47 {% if let Some(audio_url) = audio_url %}
48 <source src="{{ audio_url }}" type="audio/mpeg">
49 {% endif %}
50 </audio>
51 <audio id="media-b" preload="metadata"></audio>
52
53 <div class="player-controls">
54 <button class="play-button" id="play-btn" aria-label="Play">
55 <svg id="play-icon" width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
56 <path d="M8 5v14l11-7z"/>
57 </svg>
58 <svg id="pause-icon" class="media-icon-hidden" width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
59 <path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/>
60 </svg>
61 </button>
62 <div class="progress-container">
63 <div class="progress-bar" id="progress-bar">
64 <div class="progress-fill" id="progress-fill"></div>
65 </div>
66 <div class="time-display">
67 <span id="current-time">0:00</span>
68 <span id="duration">0:00</span>
69 </div>
70 </div>
71 </div>
72
73 <div class="insertion-label" id="insertion-label"></div>
74 <button class="skip-insertion" id="skip-btn" type="button">Skip &#8250;</button>
75
76 <div class="player-secondary">
77 <div class="speed-control">
78 <span>Speed:</span>
79 <button class="speed-button" data-speed="0.5">0.5x</button>
80 <button class="speed-button is-selected" data-speed="1">1x</button>
81 <button class="speed-button" data-speed="1.5">1.5x</button>
82 <button class="speed-button" data-speed="2">2x</button>
83 </div>
84 <div class="volume-control">
85 <span class="volume-icon">
86 <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
87 <path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z"/>
88 </svg>
89 </span>
90 <input type="range" class="volume-slider" id="volume-slider" min="0" max="100" value="75" />
91 </div>
92 </div>
93 </div>
94
95 {% if !chapters.is_empty() %}
96 <div class="chapters">
97 <h3>Chapters</h3>
98 <ul class="chapter-list">
99 {% for chapter in chapters %}
100 <li class="chapter-item" data-time="{{ chapter.start_seconds }}">
101 <span class="chapter-title">{{ chapter.title }}</span>
102 <span class="chapter-time">{{ chapter.timestamp }}</span>
103 </li>
104 {% endfor %}
105 </ul>
106 </div>
107 {% endif %}
108
109 {% if !item.description.is_empty() %}
110 <div class="media-description">
111 <p>{{ item.description }}</p>
112 </div>
113 {% endif %}
114
115 {% if !versions.is_empty() %}
116 <section class="library-downloads">
117 <h3>Source files</h3>
118 {% for version in versions %}
119 {% if version.has_file %}
120 <div class="download-row">
121 <span class="download-version">v{{ version.number }}</span>
122 {% if let Some(label) = version.label %}
123 <span class="download-label">{{ label }}</span>
124 {% else %}
125 <span class="download-label download-label--empty">{% match version.file_name %}{% when Some with (name) %}{{ name }}{% when None %}Download{% endmatch %}</span>
126 {% endif %}
127 <span class="download-size">{{ version.size }}</span>
128 <button class="btn-secondary btn-download-compact"
129 onclick="downloadVersion('{{ version.id }}')">Download</button>
130 </div>
131 {% endif %}
132 {% endfor %}
133 </section>
134 {% endif %}
135 </article>
136
137 {% include "partials/discussion_section.html" %}
138
139 <footer class="media-player-footer">
140 {% if is_owner %}
141 <a href="/dashboard/item/{{ item.id }}">Edit</a> &middot;
142 {% endif %}
143 <a href="/i/{{ item.id }}">Store page</a> &middot;
144 <a href="/library">Your library</a>
145 </footer>
146 {% endblock %}
147
148 {% block scripts %}
149 <script id="media-player-data" type="application/json">
150 {
151 "segments": {{ segments_json|safe }},
152 "mediaType": "audio",
153 "itemId": "{{ item.id }}"
154 }
155 </script>
156 <script src="/static/media-player.js"></script>
157 <script>
158 function downloadVersion(versionId) {
159 fetch('/api/versions/' + versionId + '/download')
160 .then(function(res) {
161 if (!res.ok) throw new Error('Failed to get download URL');
162 return res.json();
163 })
164 .then(function(data) {
165 window.location.href = data.download_url;
166 })
167 .catch(function(err) {
168 showToast(err.message || 'Download failed');
169 });
170 }
171 </script>
172 {% endblock %}
173