Skip to content

Commit ddd97f2

Browse files
author
Tarteel
committed
feat: quarter juz option, remove inaccurate duration/phase labels, download missing audio
1 parent 1870aa4 commit ddd97f2

File tree

7 files changed

+42
-35
lines changed

7 files changed

+42
-35
lines changed

backend/api/private_rooms.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ async def create_private_room(
4141
):
4242
if body.rakats not in (8, 20):
4343
raise HTTPException(status_code=400, detail="rakats must be 8 or 20")
44-
if body.juz_per_night not in (0.5, 1.0):
45-
raise HTTPException(status_code=400, detail="juz_per_night must be 0.5 or 1.0")
44+
if body.juz_per_night not in (0.25, 0.5, 1.0):
45+
raise HTTPException(status_code=400, detail="juz_per_night must be 0.25, 0.5 or 1.0")
4646
if not (1 <= body.juz_number <= 30):
4747
raise HTTPException(status_code=400, detail="juz_number must be 1-30")
4848

4949
juz_half = None
50-
if body.juz_per_night == 0.5:
51-
juz_half = 1
50+
if body.juz_per_night in (0.5, 0.25):
51+
juz_half = 1 # always use first slice (first half or first quarter)
5252

5353
invite_code = secrets.token_urlsafe(9)[:12]
5454

backend/services/audio/playlist_builder.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
import logging
4343
from pathlib import Path
4444
from config import get_settings
45-
from utils.juz_data import get_juz_ayahs, distribute_ayahs_to_rakats, AyahKey
45+
from utils.juz_data import get_juz_ayahs, get_juz_quarter, distribute_ayahs_to_rakats, AyahKey
4646
from services.audio.downloader import get_audio_path
4747

4848
logger = logging.getLogger(__name__)
@@ -144,12 +144,16 @@ def build_concat_file(
144144
juz_number: int,
145145
juz_half: int | None,
146146
reciter: str,
147+
juz_per_night: float = 1.0,
147148
) -> Path:
148149
"""
149150
Build a single FFmpeg concat file for a room.
150151
Returns the path to the concat .txt file.
151152
"""
152-
ayahs = get_juz_ayahs(juz_number, half=juz_half)
153+
if juz_per_night == 0.25:
154+
ayahs = get_juz_quarter(juz_number, juz_half or 1)
155+
else:
156+
ayahs = get_juz_ayahs(juz_number, half=juz_half)
153157

154158
# Al-Fatiha (Surah 1) is recited separately as a fixed step in every rakat.
155159
# Strip it from the juz portion so it is never played twice,

backend/services/scheduler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ async def build_playlist_job(room_slot_id: str) -> None:
258258
loop = asyncio.get_event_loop()
259259
concat_path = await loop.run_in_executor(
260260
None, build_concat_file,
261-
room_slot_id, slot.rakats, slot.juz_number, slot.juz_half, slot.reciter,
261+
room_slot_id, slot.rakats, slot.juz_number, slot.juz_half, slot.reciter, slot.juz_per_night,
262262
)
263263
async with AsyncSessionLocal() as db:
264264
s = await db.get(RoomSlot, uuid.UUID(room_slot_id))

backend/utils/juz_data.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,20 @@ def get_juz_ayahs(juz_num: int, half: int | None = None) -> list[AyahKey]:
106106
return all_ayahs[midpoint:]
107107

108108

109+
def get_juz_quarter(juz_num: int, quarter: int) -> list[AyahKey]:
110+
"""
111+
Return one quarter of a juz.
112+
quarter: 1 = first quarter, 2 = second, 3 = third, 4 = fourth.
113+
"""
114+
info = get_juz_info(juz_num)
115+
all_ayahs = get_ayahs_in_range(info.start_surah, info.start_ayah, info.end_surah, info.end_ayah)
116+
total = len(all_ayahs)
117+
size = total // 4
118+
start = (quarter - 1) * size
119+
end = start + size if quarter < 4 else total
120+
return all_ayahs[start:end]
121+
122+
109123
def distribute_ayahs_to_rakats(ayahs: list[AyahKey], num_rakats: int) -> list[list[AyahKey]]:
110124
"""Distribute ayahs as evenly as possible across rakats."""
111125
total = len(ayahs)

frontend/app/dashboard/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ export default function DashboardPage() {
319319
>
320320
<div className="flex items-center justify-between mb-1">
321321
<span className="text-sm font-medium text-white">
322-
{r.rakats}R · Juz {r.juz_number} {r.juz_per_night === 0.5 ? "(½)" : ""}
322+
{r.rakats}R · Juz {r.juz_number}{r.juz_per_night === 0.25 ? " (¼)" : r.juz_per_night === 0.5 ? " (½)" : ""}
323323
</span>
324324
<span className={`text-xs px-2 py-0.5 rounded-full ${
325325
r.status === "live" ? "bg-green-900/40 text-green-400" : "bg-gray-800 text-gray-400"
@@ -389,7 +389,7 @@ export default function DashboardPage() {
389389
<div>
390390
<label className="text-xs text-gray-400 mb-1 block">Amount</label>
391391
<div className="flex gap-2">
392-
{[1.0, 0.5].map((j) => (
392+
{([1.0, 0.5, 0.25] as const).map((j) => (
393393
<button
394394
key={j}
395395
onClick={() => setCreateForm((f) => ({ ...f, juz_per_night: j }))}
@@ -399,7 +399,7 @@ export default function DashboardPage() {
399399
: "border-gray-700 text-gray-400"
400400
}`}
401401
>
402-
{j === 1.0 ? "Full Juz" : "Half Juz"}
402+
{j === 1.0 ? "Full" : j === 0.5 ? "Half" : "Quarter"}
403403
</button>
404404
))}
405405
</div>

frontend/app/room/[id]/page.tsx

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -94,20 +94,14 @@ function PrayerProgress({
9494
const netTotal = Math.max(total - breaks.length * INTER_PRAYER_BREAK, 1);
9595
const timePerRakah = netTotal / totalRakats;
9696
const currentRakah = Math.min(Math.floor(netCurrent / timePerRakah) + 1, totalRakats);
97-
const timeInRakah = Math.max(0, netCurrent - (currentRakah - 1) * timePerRakah);
98-
const isFirstOfPrayer = currentRakah % 2 === 1;
99-
const phase = getPrayerPhase(timeInRakah, timePerRakah, isFirstOfPrayer);
10097
const remaining = Math.max(0, total - current);
10198

10299
return (
103100
<div className="w-full space-y-3">
104-
{/* Rakah + phase label */}
105-
<div className="flex items-center justify-between">
106-
<div className="flex items-baseline gap-1">
107-
<span className="text-mosque-gold font-bold text-base">Rakah {currentRakah}</span>
108-
<span className="text-gray-500 text-xs">of {totalRakats}</span>
109-
</div>
110-
<span className="text-xs text-gray-400 font-medium">{phase}</span>
101+
{/* Rakah label */}
102+
<div className="flex items-baseline gap-1">
103+
<span className="text-mosque-gold font-bold text-base">Rakah {currentRakah}</span>
104+
<span className="text-gray-500 text-xs">of {totalRakats}</span>
111105
</div>
112106

113107
{/* Segmented bar — one segment per rakah */}
@@ -263,7 +257,9 @@ export default function RoomPage({ params }: { params: { id: string } }) {
263257
</div>
264258
);
265259

266-
const juzLabel = room.juz_half === 1
260+
const juzLabel = room.juz_per_night === 0.25
261+
? `Juz ${room.juz_number} — ¼`
262+
: room.juz_half === 1
267263
? `Juz ${room.juz_number} — 1st half`
268264
: room.juz_half === 2
269265
? `Juz ${room.juz_number} — 2nd half`

frontend/components/RoomCard.tsx

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import Link from "next/link";
22
import type { RoomSlot } from "@/lib/api";
3-
import { ROOM_DURATION } from "@/lib/api";
43

54
const ROOM_CONFIG: Record<string, { icon: string; label: string; color: string }> = {
65
"8_1.0": { icon: "🌙", label: "Short · Full Juz", color: "from-indigo-900/40 to-mosque-dark/60" },
@@ -15,9 +14,8 @@ interface Props {
1514
}
1615

1716
export default function RoomCard({ room }: Props) {
18-
const key = `${room.rakats}_${room.juz_per_night}`;
19-
const config = ROOM_CONFIG[key] ?? { icon: "🌙", label: "", color: "from-mosque-navy to-mosque-dark" };
20-
const duration = ROOM_DURATION[key] || 60;
17+
const key = `${room.rakats}_${room.juz_per_night}`;
18+
const config = ROOM_CONFIG[key] ?? { icon: "🌙", label: "", color: "from-mosque-navy to-mosque-dark" };
2119

2220
const juzLabel =
2321
room.juz_half === 1 ? `Juz ${room.juz_number} · 1st half`
@@ -85,16 +83,11 @@ export default function RoomCard({ room }: Props) {
8583
<span className="text-mosque-gold text-xs"></span>
8684
<span>{juzLabel}</span>
8785
</div>
88-
<div className="flex items-center gap-2 text-xs text-gray-500">
89-
<span></span>
90-
<span>~{duration} min</span>
91-
{room.participant_count > 0 && (
92-
<>
93-
<span className="text-gray-700">·</span>
94-
<span className="text-white/70">{room.participant_count} praying</span>
95-
</>
96-
)}
97-
</div>
86+
{room.participant_count > 0 && (
87+
<div className="flex items-center gap-2 text-xs text-gray-500">
88+
<span className="text-white/70">{room.participant_count} praying</span>
89+
</div>
90+
)}
9891
</div>
9992

10093
{/* CTA */}

0 commit comments

Comments
 (0)