From 6e5b4850b9cd8ae075c27425c4f54c7be2ed24d2 Mon Sep 17 00:00:00 2001 From: patel-mann Date: Sun, 15 Feb 2026 00:24:49 -0700 Subject: [PATCH] feat: audio playback working with bugs --- .../__pycache__/api_routes.cpython-314.pyc | Bin 32006 -> 32124 bytes backend/api_routes.py | 45 ++++++++++-------- frontend/src/api.js | 4 ++ frontend/src/components/AudioPostCard.jsx | 26 +++++++++- 4 files changed, 53 insertions(+), 22 deletions(-) diff --git a/backend/__pycache__/api_routes.cpython-314.pyc b/backend/__pycache__/api_routes.cpython-314.pyc index 3aa3bae7dbf6ceebc2889f9fc66750af8994543c..1ffef2e4734c213baf6b207a18a350a67d1f53f8 100644 GIT binary patch delta 5145 zcmahN3vg7`_1?Gp-sYc>Z2k$^O-SORgb*MEh=xECAb|i&2mvC?l3kM3d^UG?C4e=d zen@RK!K;E|KTaJ1s~wotwpg_O+Uhv9FrjLnzogpnZv&zTV?XEIxBIe-wB2D(&OP`1 z@44q(_Kr*R+eZHUt0-p)C-_zQ-KPfA&%|wWk3=zBIc)5epZG4ozL7luysz|XH zWfJ?0Owyg=ufBfP+CBXF!ZiM#lp$XSt3k0!G~`eXTdZRqLt@fAGy)(0(y*X));y}w z>}8;KHmcRRmW8?atGb$tk!tG04AyzNn)#rnGD=ND*st6LI#*RD(akImYl+xj7_q-1 zyiXVLXN|e?N?6~AaatGY`mP15*F{-v3QJjQbgm`9RU5^%O6OXtb1eg|x+t#Iz$NlY z>-pVs0Tns7*6R{0Kw@Q-#2S!z7=5IT{A*pJQI}W^63tN(c3ol(->QUdtkEUxAhAA5 zqDA+rh5td9XwfA$f<#-CL~B?gcazSwIU>^=VPC`+vA-o^zb(99)UL~44_3BDS=nT# zMZ~r}__%3FBA-ajpniTlkjcGk69(HQt=I@@H-@pf(lt3@oDjRzt}p&S_iPC)8CV5~ zs77y}&(G!pYBQ+DI!}k6)gs+c+q=s~8I~I#SeD+_0f=uVfFW{$#L$$p2p%#&)bYsN zgIzzKK8ef6>z+(|vg26R6U}0mEdch8&_w~15E;G(LX2My12RNf$y&<$nR!r8i&WJ{ zh-(HRc_f##1FQCFC3ZtpxJJ9YAe0m@ZKZZ4au>KwB_P;Jip1AK3P?U_fz@I|KJidn z%|9*u&=WC`z)9ai)V+0xt*?EYze4QWr3?E+;VtaAEzWi zvZxy89`JgY+vo7~s&SewM}T#!vCaVKL)0dzhEBJirGgfl#4v?woVM$7`kg+Epv1!F z58_6W!|8H4bXnF1M3MA$fDDmwQ~bVxy#u2$W2Wqb8_$?#OiISsX>VuGK4v_&>8a$G z>&|4a8*Y5lGV@6FUo2I_b)&hb6Y?h&l8`dgG(2yp>6=NJ#!h=PA?3~Z<)fUPO&@+ z#67{2IX%X~oya6O_k`Km_Yk`Uz;5%(-kY4=9+#zqxn1sFzsK3_EA#A!!eAIVyA{E0 z$Qc*#xmj6vU#F*+-Hy~d5DX#Mi{SeJYzZs|aV#O>Ci?+DQjovmPF(*G0Sp0RGqSr7 z*WDMYd`6&I;KSwv0w?A5I5cbb!1^cf_1XCIIsL&~EKeuUTlm6RmGoBbo%OBEx?qzB z${77Q|ETOS6YGREyNw^3olkG)CuZ+R+XsZ}@I+O`8`$3M>0k$VMR}=$nFJRd<-eeh z@%TBlismS{&zZgUN3dthQRN=D%i~nzF?uL42XpRZ4ivi&!TkshA`pqfl&GZ!w}9}w;PHy96^QGd zsuJ2BG%ox#9UKQb`v8Dy>ViY=W4jo}(`<_u$ySX{*6Gu-y%bsV5MZ8FLl2a{p~pGE zFc?;k02_!+*4!OOg3v6Z&psv`!$wdtbr5{f;#B!lplu1RTij?E6jg-L9;NJkR4|TU z0s*SpolzP}RjI|}gR9yffQ#rV)5_;z}o@s%YnIOmw>b{ z*jX2#<>yiAJH93Z^-8|7uXsX(RsI&XM|f>p8Q<2hnqJ_)Y{-^U4SY^@72npG$+MQf zU*0O&l?4XigOAtPYADrE;#z|9pw*Z{Hpl^~j0B_x7+w7v`1952d@v(6$!@~2(U~pS zWF%i36CNq82C)Zf^GH)U3I<^!LAluzvOUQ>;!DD>AuJ>k)paM@K7-9=6A z2rvenSsXQVAod05fl3*NKca%QCiE08MX`K>*ggQw;dbo)5J}>~h`R!ZLBtHZ;$6|p z8R}>pIq{lkM*fA^SpX{aijk>x4D9(93tzP|FP=cHAHsTXqn$|xDjST?j~(wHO{y`5a}vzF%;ok?yUHeP_anETg)qOr8z z2mA-EhXW4>jH77DR?@}q zZ7PyuAfr$6lTAKF0xTr>OmM@h`E-?NR5FrLN9f>95#0*zM1;z#$MqWYxZXwVGBwxJxU`qsV@H98k?L9yD#%E*6>Kd)4{`QWkcDN zgJNWgLd8O6Hz4pJm;m4zT1N>C=ZW^fB0toTr%{7r$zTw52{#f=aRHxlwS&JhJDVGg# zNIOzTc+O^voCj(z@S4rfMQD?V6fD`&Na;@A-+omC4zushGLcEW;+RD@gjs{>ENt9` z)B-R8*4}HM5oZDP-|+PXV5$}HYp(xoq}fD?Cv82P6)NdzC_809q5MxzyQC2FrN(%AVZ!>|uF!dCZ?4b-mv3Q&q(vv=Xz1L=aZBhM;lZv#c|JHWFQ|4+{I`cfA_`ogkl~^5=?#T( zRUlsjqb&5Ask*U+UI^ZGqe=%w(BeXit1S%c(Wpjv$os;@G{qo16Y(AWAMxQs`Lg)J zS=LpV!cl(SQzTb|myN;n-m=IGth9mOtRmRk*F@(;yT=xR0JJp`gV>sKj4g&eHjjVe zEtG2j-N>i)4@#AkznhR1JQj$hGq6l-TUae@swRiS)z{&0u=VKkod8s+&&Qq)zH?I| zr6>8PI}7NuJbs{Xun!r;K?V@(K!E>gWA`9<3jw~l**OIGh!l@JhA%aSGnU~9f&40{*D7Q?V1s0MGq&+xJ`yl)II2gAZ=RxCw^<-qVv8K#V3e5?{Iz;6q+ z{I!AX_)S28f5dm*5Ii>^Q`1)|@KeHMR`BAkBsx8VNbI=wWTr)58i9t$1Of;lLJ|niAS@9~N!TpeC0yBLmv46=32H)o zp^g$bZT-;}A3vOI2s>)?N~`M`2}zQtrckQaP$}Ao=XcXNykeRouhHK=GLsI7ilf<<}=US_CH6d4X64zGb5>a%v=v?cN zYkd+|+l(&O0{i7k_Jr_pqb{)tB{nBXY}X|=!t1(3n=Y{pC2mWS@aPh4u-say<<{xZ zC3d1jdy+)EF5!XGGZLk{b%{F+@!vJWYdG&PobR4FcYZ^c--8xAlPumb!&Tj-bL~Yg zZxUCBhgD0i9`(MIwHa_%;Tv)<)^b4g6!lo-xVbkFg%`_q76g&V`w_%#fdM5FRr-7R z092LN%J(Aikh-Ct>>_@M zm|_GD6Q?~E32=8X)T{LKBSak_zzIYML=m{sxsC840!Inl1-Dd{Z@in(dkA0;OD+o^ zAzXJ~sESyS=D>$9LnNN+3oBmD+Wq+cP5h0xU`P2OHM9D~bQXr+ELzTDrxt&i8czv_ zB2lm62Uf?~;}EU;!MwQ413v$t=6NBVyJO*C$me%QxvxLc!njV6qY!_Hz#{}itOj6h zeF-}Sd+L`mfD`q7Y!v=ozfzt+j-RLv%NAH|#5c}(Gn_kI$vo;~4L@b`E|P+`5yVpt zU?xL+kdt5ATxlYraf^@lM)*+(HkQvHCf@Y~nh>~T&G4)SnZ7r|N8xN^t$Z0-cBp@B z+-xor`A-=C1BnZfw+MS1nwrWmsC%1=hKVC?KNJh_L2ooL5Y^Z;-*_Mr3!-k#w?wul zNrM(G!kP&P_sR*QjPpMc_%i|Fyztw~|3U;ZXE0&0^3V}-{#sLw`~jYIsWr{rre;yb zRI=NIahJ_dX~IM{>G>f6Jv74DxJj$FQTXLLr~DBys2{JhGVAXNy&#fs4-~b6^-&@` z24`Cft-`4Rc)hhk{yS3dR;OEIZ1ELDziv2-#BN}ie+b{+=#;1N_LU?-9`;Oh!9_tFKe|*B0&NL0fEtay&;*oYqFt)lFCbm?lNs^LUToA}Z*YpH~k^TAb&}}D*A496k%=76`xJ(~|6#`JVy;{D8FQ=hn zd&K$=V$f>i`|2m#m$5cce_s%pbR?pbVkkm#YgVVB&MjQ{GD7E935-f`ELI4QcPdeRh%0UF|N}gwj8TN7}(C_>#KrcG^pMD%5LGYjH(memSG5)=yWmWMRkb6 znCEk&JQ zF?cfYf8T&fhtnK*E?p^kgXOTI7tBTWt``-Ou%P4~cZqN<5jE;q>tP4@zWrDx0yBRKq+StcxbvTZf84{ws zU|7cb=fo{|3yaLuo0uW9u89vE|34pCLRymxV(%GD+c*JI zn=uW19v;C{2cL%V=reV~2i-}&h(0XQ@z=S>7vmXs!APviPFL zNkb*@YJ16bEnl{k7^yf%q|c>HwtAYtcO&8tcyBK?1+^{i*j2EGc}$&FWEGFcyn<#8 zK4DfWb*5^#B<*G%%L-IVPuMiO=69Lkd)-b*jWw7$b73r42b0~Ikmkvn-$|QFJXXn% zu`H9q=1#hBh{t1VH>XQWOr8|@YDevG>d+$lhCtNeb3~Nh{(#?cyJxFoe~3Gx2Lg@| zHj~ot3p#u;zY=oL)N|W8jbLsiK%sOojX-^=4gR6xRv&Wu9np{jJ2D}RdRI9TH`CcG z!mxJpyKVvKbsbOl#PI)%zQB6ULtNCvOi_tNU>A;g{l2IVD;^_H_aPkDiT?+3XkOD$ z(AZ);$XBA@TPR1TkaI*@^_gADEq=W0WZ9)0=NmcBCvSe<@{6StInE0?8%8!yS#0v^ z$$|yXZg{g`%W?Z;)||1S3t3eYwyG~C)8MI!BJ*TgA-sEM;qYWy{$~!3eKOCs-%&CHc+i z=JQs<&!-dpe6hewGMnAz^YaDnmXY3rpCZvOR(NV=n!5IIDyyNX=Gwy>kRDI*di|ju zub1ycBB%dE;-*l9|48jSlEK(nI66?tRQS$7)v#C_y~JRlOBTPE1nws=LEtih4+w~> zFx_rBT~Ya`1g;^7%VA%%=K!a>38(7?r>!hjB&TH-H-}?UPK$=q!r(OF#f;`OFsnGt zC{BZhQ!jIBIgu*992H`TH9*&3aauc)@kfeeSkz;KGBZu(tB($4GUz^Pn{&MA0?VB? z*;)0cX33N{Mc7n{`ohr{*!(F|3cln@Ci%g_V}%o`wHMgj&rH~v#5(U2!uSeQ{9lA3 BS|R`d diff --git a/backend/api_routes.py b/backend/api_routes.py index bfce130..13cc0e1 100644 --- a/backend/api_routes.py +++ b/backend/api_routes.py @@ -96,6 +96,16 @@ def _build_prompt(transcript_text: str, title: str) -> str: f"{transcript_text}\n\n" "Answer user questions grounded in this transcript." ) +def _add_audio_url(post: Dict[str, Any]) -> Dict[str, Any]: + """Add signed audio URL to post if ready""" + if post.get("status") == "ready": + try: + audio_data = get_original_audio_url(post["post_id"], expires_in=3600) + post["audio_url"] = audio_data["signed_url"] + except: + pass + return post + @api.get("/health") @@ -397,20 +407,32 @@ def api_create_post(): return _error(str(e), 500) + @api.get("/posts") def api_list_posts(): page = request.args.get("page", default=1, type=int) limit = request.args.get("limit", default=20, type=int) visibility = request.args.get("visibility") - user_id = request.args.get("user_id", type=int) - + current_user_id = request.args.get("current_user_id", type=int) # NEW LINE + try: - rows = list_audio_posts(page=page, limit=limit, visibility=visibility, user_id=user_id) + rows = list_audio_posts(page=page, limit=limit, visibility=visibility) + + # NEW: Filter private posts + if current_user_id: + rows = [p for p in rows if p.get('visibility') == 'public' or p.get('user_id') == current_user_id] + else: + rows = [p for p in rows if p.get('visibility') == 'public'] + + # NEW: Add audio URLs - CHANGE THIS LINE ONLY + rows = [_add_audio_url(post) for post in rows] + return jsonify({"posts": rows, "page": page, "limit": min(max(1, limit), 100)}) except Exception as e: return _error(str(e), 500) + @api.get("/posts/") def api_get_post(post_id: int): row = get_audio_post_by_id(post_id) @@ -583,20 +605,3 @@ def api_post_audit(post_id: int): return jsonify({"logs": list_audit_logs(post_id=post_id, page=page, limit=limit)}) except Exception as e: return _error(str(e), 500) - -@api.get("/posts//audio") -def api_get_audio_url(post_id: int): - """ - Get a signed URL for the original audio file. - """ - expires_in = request.args.get("expires_in", default=3600, type=int) - - try: - audio_data = get_original_audio_url(post_id, expires_in=expires_in) - return jsonify(audio_data) - except ValueError as e: - return _error(str(e), 404) - except RuntimeError as e: - return _error(str(e), 500) - except Exception as e: - return _error(f"Failed to get audio URL: {e}", 500) diff --git a/frontend/src/api.js b/frontend/src/api.js index 9e0ef6f..9bcd624 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -137,6 +137,10 @@ class ApiClient { return this.request(`/posts/${postId}/metadata`); } + async getAudioUrl(postId, expiresIn = 3600) { + return this.request(`/posts/${postId}/audio?expires_in=${expiresIn}`); + } + // ==================== Post Files ==================== async getPostFiles(postId) { diff --git a/frontend/src/components/AudioPostCard.jsx b/frontend/src/components/AudioPostCard.jsx index 3bba9ab..33bcac0 100644 --- a/frontend/src/components/AudioPostCard.jsx +++ b/frontend/src/components/AudioPostCard.jsx @@ -12,6 +12,13 @@ export default function AudioPostCard({ post }) { const [transcriptExpanded, setTranscriptExpanded] = useState(false) const audioRef = useRef(null) + // DEBUG: Log post data to console + useEffect(() => { + console.log('Post data:', post) + console.log('Audio URL:', post.audio_url) + console.log('Status:', post.status) + }, [post]) + const formatDate = (dateString) => { const date = new Date(dateString) const now = new Date() @@ -35,7 +42,14 @@ export default function AudioPostCard({ post }) { if (post.status === 'ready' && !transcript && !loadingTranscript) { loadTranscript() } - }, [post.post_id, post.status]) + + // Set audio source if available + if (post.audio_url && audioRef.current) { + console.log('Setting audio src to:', post.audio_url) + audioRef.current.src = post.audio_url + audioRef.current.load() // Force reload + } + }, [post.post_id, post.status, post.audio_url]) const loadTranscript = async () => { setLoadingTranscript(true) @@ -78,10 +92,16 @@ export default function AudioPostCard({ post }) { const handleLoadedMetadata = () => { if (audioRef.current) { + console.log('Audio metadata loaded, duration:', audioRef.current.duration) setDuration(audioRef.current.duration) } } + const handleAudioError = (e) => { + console.error('Audio error:', e) + console.error('Audio element error:', audioRef.current?.error) + } + const handleSeek = (e) => { const rect = e.currentTarget.getBoundingClientRect() const x = e.clientX - rect.left @@ -155,11 +175,13 @@ export default function AudioPostCard({ post }) { {/* Hidden audio element */}