Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions books/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,10 @@ def generate_qr_short_code():
def generate_qr_password():
from registration.wordlist import WORDS

pin = "".join(secrets.choice(QR_CODE_ALPHABET) for _ in range(4))
word1 = secrets.choice(WORDS).lower()
word2 = secrets.choice(WORDS).lower()
return f"{pin}-{word1}-{word2}"
digits = "".join(secrets.choice("0123456789") for _ in range(2))
return f"{word1}-{word2}-{digits}"


class QRCode(models.Model):
Expand Down
12 changes: 11 additions & 1 deletion books/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,17 @@ def test_generate_qr_short_code_length(self):
assert len(code) == 8

def test_generate_qr_password_format(self):
from registration.wordlist import WORDS

pw = generate_qr_password()
parts = pw.split("-")
assert len(parts) == 3
assert len(parts[0]) == 4

# First two parts should be words from the wordlist (lowercased)
wordlist_lower = [w.lower() for w in WORDS]
assert parts[0] in wordlist_lower
assert parts[1] in wordlist_lower

# Last part should be exactly 2 digits
assert parts[2].isdigit()
assert len(parts[2]) == 2
12 changes: 12 additions & 0 deletions books/tests/test_playback.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,18 @@ def test_playback_no_password_shows_player(self):
assert resp.status_code == 200
assert b"audio-player" in resp.content

def test_narrator_shown_before_password_entry(self):
recording = Recording.objects.create(
book=self.book, narrator=self.narrator, status=RecordingStatus.READY
)
QRCode.objects.create(
book=self.book, recording=recording, label_text="test", password="secret"
)
resp = self.client.get(f"/b/play/{self.book.id}/")
assert resp.status_code == 200
assert b"Read by: Play User" in resp.content
assert b"audio-player" not in resp.content

def test_session_unlock_persists(self):
recording = Recording.objects.create(
book=self.book, narrator=self.narrator, status=RecordingStatus.READY
Expand Down
2 changes: 1 addition & 1 deletion templates/books/playback.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ <h2>{{ book.title }}</h2>
<p>by {{ book.author }}</p>
{% if book.illustrator %}<p>Illustrated by: {{ book.illustrator }}</p>{% endif %}
{% if not book.public_domain and book.publisher %}<p>Published by: {{ book.publisher }}</p>{% endif %}
<p class="narrator">Read by: {{ recording.narrator.name }}</p>
</div>

{% if password_required and not password_valid %}
Expand All @@ -22,7 +23,6 @@ <h2>{{ book.title }}</h2>
<button type="submit" class="btn btn-primary">Unlock</button>
</form>
{% elif password_valid %}
<p class="narrator">As read by: {{ recording.narrator.name }}</p>
<audio id="audio-player" controls>
<source src="{% url 'books:serve_recording' recording_id=recording.id %}" type="audio/webm">
<p>Sorry, your browser does not support audio playback.</p>
Expand Down