Ë
    º4´jp#  ã                   ó@  — d dl Z d dlZd dlZd dlZd dlZd dlmZ d dlmZm	Z	 d dl
mZ d dlmZmZmZ  ed«      Zefd„Zdd„Zd	„ Zdd
„Z G d„ d«      Z G d„ d«      Z G d„ d«      Zefd„ZdZedk(  r* e«       Z edd¬«        edde«      j;                  «        yy)é    N)ÚPath)Úparse_qsÚurlparse)Úmake_server)ÚAGENTSÚevaluate_ratingsÚ	readinessz%/root/workspace/shorts_data/shorts.dbc                 óÊ   — t        | «      } | j                  j                  dd¬«       t        j                  | «      5 }|j                  d«       d d d «       y # 1 sw Y   y xY w)NT)ÚparentsÚexist_okaÈ  
        CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT NOT NULL);
        CREATE TABLE IF NOT EXISTS candidates (
          id INTEGER PRIMARY KEY AUTOINCREMENT, source_url TEXT, title TEXT NOT NULL,
          clip_path TEXT, status TEXT NOT NULL DEFAULT 'pending', score REAL,
          ratings_json TEXT NOT NULL DEFAULT '[]', warnings_json TEXT NOT NULL DEFAULT '[]',
          created_at INTEGER NOT NULL, published_at INTEGER
        );
        INSERT OR IGNORE INTO settings(key,value) VALUES ('threshold','85');
        INSERT OR IGNORE INTO settings(key,value) VALUES ('publishing_enabled','0');
        INSERT OR IGNORE INTO settings(key,value) VALUES ('source_url','');
        )r   ÚparentÚmkdirÚsqlite3ÚconnectÚexecutescript)ÚpathÚdbs     ú#/root/workspace/shorts_dashboard.pyÚinit_dbr      sW   € Ü�‹:€D�t—{‘{×(Ñ(°ÀÐ(ÔEÜ	�‰˜Ó	ð  "Ø
×Ñð ô 	÷÷ ñ ús   ¾AÁA"c                 ó  — | D �cg c]  }t        |«      ‘Œ } }| sy|€| d   n
t        |«      }t        | «      t        | «      z  |k\  xr> t        | «      t        | «      dz     |dz
  k\  xr |dk\  xr t        d„ | D «       «      dk\  S c c}w )NFéÿÿÿÿé   é   éZ   c              3   ó&   K  — | ]	  }|d k\  –— Œ y­w)éP   N© )Ú.0Úss     r   ú	<genexpr>z"score_candidate.<locals>.<genexpr>"   s0   è ø€ ò  Xð  LMð  @Að  EGõ  @Gñ  Xùs   ‚é   )ÚfloatÚsumÚlenÚsorted)ÚscoresÚ	thresholdÚsafety_scorer   Úsafetys        r   Úscore_candidater*      s»   € Ø &Ö'˜1Œe�A�hÐ'€FÐ'Ù˜%Ø'Ð/ˆV�BŠZ´U¸<Ó5H€FÜˆv‹;œ˜V›Ñ$¨	Ñ1ò  ]´f¸V³nÄSÈÃ[ÐRSÁ^Ñ6TÐXaÐdeÑXeÑ6eò  ]ÐjpÐtvÑjvò  ]Ô{~ñ  Xð  QWô  Xó  |Xð  \]ñ  |]ð  ]ùò (s   …Bc                 óX   — | j                  d«      D ��ci c]  \  }}||“Œ
 c}}S c c}}w )NzSELECT key,value FROM settings)Úexecute)r   ÚkÚvs      r   Ú	_settingsr/   $   s(   € ØŸ:™:Ð&FÓG×H‘C�A�aˆAˆa‰CÓHÐHùÓHs   •&c           
      óÂ   — t        j                  |�|ni «      j                  «       } | ddddœj                  ||› d�«      ddt	        t        |«      «      fg«       |gS )Nú200 OKz201 Createdz404 Not Found)éÈ   éÉ   é”  z Error)úContent-Typezapplication/jsonúContent-Length)ÚjsonÚdumpsÚencodeÚgetÚstrr$   )ÚstartÚstatusÚbodyÚdatas       r   Ú_jsonr@   '   s�   € Ü�:‰:˜dÐ.‘d°BÓ7×>Ñ>Ó@€DÙ	ˆx˜M¨oÑ
>×
BÑ
BÀ6ÈfÈXÐU[ÐK\Ó
]ð  aDð  FVô  WZô  [^ð  _có  [dó  Weð  Efð  `gô  hØˆ6€Mó    c                   ó   — e Zd Zd„ Zd„ Zy)ÚResponsec                 ó    — || _         || _        y ©N)Ústatus_codeÚ_body)Úselfr=   r>   s      r   Ú__init__zResponse.__init__-   s   € °v dÔ&6È$¸t½zrA   c                 ó@   — t        j                  | j                  «      S rE   )r7   ÚloadsrG   ©rH   s    r   Úget_jsonzResponse.get_json.   s   € œtŸz™z¨$¯*©*Ó5Ð5rA   N)Ú__name__Ú
__module__Ú__qualname__rI   rM   r   rA   r   rC   rC   ,   s   „ ÚNÛ5rA   rC   c                   ó   — e Zd Zd„ Zd„ Zy)Ú
TestClientc                 ó   — || _         y rE   )Úapp)rH   rT   s     r   rI   zTestClient.__init__1   s   €  c˜T�XrA   c                 óÆ   ‡‡— g Šg Šdj                  | j                  j                  d|d dœˆˆfd„«      «      }t        t	        ‰d   j                  «       d   «      |«      S )NrA   ÚGET)ÚREQUEST_METHODÚ	PATH_INFOú
wsgi.inputc                 óH   •— ‰j                  | «      ‰j                  |«      fS rE   )ÚappendÚextend)r   ÚhÚheadersÚresults     €€r   ú<lambda>z TestClient.get.<locals>.<lambda>4   s>   ø€ Ðou×o|Ño|Ð}~Óoð  BI÷  BPñ  BPð  QRó  BSð  oT€ rA   r   )ÚjoinrT   Ú_wsgirC   ÚintÚsplit)rH   r   r>   r^   r_   s      @@r   r:   zTestClient.get2   s^   ù€ Øˆ˜2�7Ø�X‰X�d—h‘h—n‘n°uÈÐ[_Ñ%`ô  cTó  Uó  VˆÜœ˜F 1™IŸO™OÓ-¨aÑ0Ó1°4Ó8Ð8rA   N)rN   rO   rP   rI   r:   r   rA   r   rR   rR   0   s
   „ Ú)ó9rA   rR   c                   ó*   — e Zd Zd„ Zd„ Zd„ Zd„ Zd„ Zy)ÚAppc                 ó2   — t        |«      | _        d| _        y )NF)r   r   Útesting)rH   r   s     r   rI   zApp.__init__8   s   € ¤D¨£H˜DœG¸5¨d­lrA   c                 ó   — t        | «      S rE   )rR   rL   s    r   Útest_clientzApp.test_client9   s   € ¤*¨TÓ"2Ð2rA   c                 ó€  — |j                  dd«      }t        j                  | j                  «      5 }|j	                  d«      rì|j                  d|j                  dd«      d   f«      j                  «       }|r|d   st        |dd	d
i«      cd d d «       S t        |d   «      j                  «       }|j                  t        d«      «      r|j                  «       st        |dd	d
i«      cd d d «       S |j                  «       } |dddt        t        |«      «      fg«       |gcd d d «       S |dk(  rt        |ddi¬«      cd d d «       S |dk(  rt        |t!        «       ¬«      cd d d «       S |dk(  r0t        |t"        D ��	cg c]
  \  }}	||	dœ‘Œ c}	}¬«      cd d d «       S |dk(  r»t%        |«      }
t'        |j                  d«      «      }t        |t)        |
d   «      |
d   dk(  |
d   ||
j                  dd«      |
j                  d«      |
j                  d«      |
j                  d d!«      t+        j,                  |
j                  d"d#«      «      d$œ	¬«      cd d d «       S |d%k(  rž|j                  d&«      D �cg c]  }t'        t/        g d'¢|«      «      ‘Œ }}|D ]P  }t+        j,                  |j1                  d(«      «      |d(<   t+        j,                  |j1                  d)«      «      |d)<   ŒR t        ||¬«      cd d d «       S |d*k(  rÐ|j                  d+«      d,k(  r¼t3        |j                  d-«      xs d«      }t+        j,                  |d.   j5                  |«      xs d/«      }d0D ]'  }||v sŒ|j                  d1|t        ||   «      f«       Œ) d|v r|j                  d1d|d   rdnd2f«       |j7                  «        t        |ddi¬«      cd d d «       S |d%k(  �rŽ|j                  d+«      d,k(  �ryt3        |j                  d-«      xs d«      }t+        j,                  |d.   j5                  |«      xs d/«      }|j                  d(g «      }|r2t9        |t)        t%        |«      d   «      |j                  d)g «      «      nd }|r|j:                  nd }|r|j<                  rd3nd4}|j                  d5|j                  dd!«      |j                  d6d7«      |j                  d8d!«      ||t+        j>                  |«      t+        j>                  |j                  d)g «      «      t3        tA        j@                  «       «      f«       |j7                  «        t        |d9d||d:œ¬;«      cd d d «       S |dk(  r| jC                  ||«      cd d d «       S 	 d d d «       t        |dd	d<i«      S c c}	}w c c}w # 1 sw Y   Œ#xY w)=NrX   ú/z/api/preview/z+SELECT clip_path FROM candidates WHERE id=?é   r   r   r4   Úerrorz
No previewz!/root/workspace/shorts_data/mediar1   )r5   z	video/mp4r6   z/api/healthÚokT)r>   z/api/readinessz/api/agents)ÚidÚmissionz/api/summaryz6SELECT status,count(*) FROM candidates GROUP BY statusr'   Úpublishing_enabledÚ1Ú
source_urlÚworker_statusÚunknownÚscan_startedÚ	last_scanÚ
last_errorÚ Úlast_resultz{})	r'   rr   rt   Úcountsru   rw   rx   ry   r{   z/api/candidatesz€SELECT id,source_url,title,clip_path,status,score,ratings_json,warnings_json,created_at FROM candidates ORDER BY created_at DESC)	rp   rt   ÚtitleÚ	clip_pathr=   ÚscoreÚratingsÚwarningsÚ
created_atr€   r�   z/api/settingsrW   ÚPOSTÚCONTENT_LENGTHrY   s   {})r'   rt   z7INSERT OR REPLACE INTO settings(key,value) VALUES (?,?)Ú0ÚapprovedÚpendingz~INSERT INTO candidates(source_url,title,clip_path,status,score,ratings_json,warnings_json,created_at) VALUES (?,?,?,?,?,?,?,?)r}   ÚUntitledr~   r3   )ro   r=   r   )r=   r>   z	not found)"r:   r   r   r   Ú
startswithr,   ÚrsplitÚfetchoner@   r   ÚresolveÚis_relative_toÚis_fileÚ
read_bytesr;   r$   r	   r   r/   Údictr"   r7   rK   ÚzipÚpoprc   ÚreadÚcommitr   Úaverager†   r8   ÚtimeÚ_page)rH   Úenvironr<   r   r   ÚrowÚtargetr?   Úkeyrq   r   r|   ÚrÚrowsÚlengthÚpayloadr-   Úpr€   Úgater   r=   s                         r   rb   z	App._wsgi:   sC  € Ø�[‰[˜ SÓ)ˆÜ�_‰_˜TŸW™WÓ%ð  	9¨Ø�‰˜Ô/Ø—j‘jÐ!NÐQU×Q\ÑQ\Ð]`ÐabÓQcÐdfÑQgÐPiÓj×sÑsÓu�Ù # a¢&´°u¸SÀ'È,ÐAWÓ1X÷ 	9ñ  	9ô ˜c !™f›×-Ñ-Ó/�Ø×,Ñ,¬TÐ2UÓ-VÔWÐ_e×_mÑ_mÔ_oÔx}ð  Dð  EHð  JQð  R^ð  I_ó  y`÷ 	9ñ  	9ð ×(Ñ(Ó*�Ù�hÐ <Ð>NÌsÔSVÐW[ÓS\Ë~Ð=^Ð_Ô`Ø�v÷ 	9ñ  	9ð �}Ò$¬U°5ÀÀT¸{Ô-K÷ 	9ñ  	9ð Ð'Ò'´°eÄ)Ã+Ô0N÷ 	9ñ  	9ð �}Ò$¬U°5Ôtz×?{ÑdpÐdgÐipÀsÐW^Ó@_Ó?{Ô-|÷ 	9ñ  	9ð �~Ò%Ü˜B“-�¬¨R¯Z©ZÐ8pÓ-qÓ(r Ü˜U´e¸A¸k¹NÓ6KÐabÐcwÑaxÐz}Ña}ð  LMð  NZñ  L[ð  ekð  |}÷  |Añ  |Að  BQð  R[ó  |\ð  lm÷  lqñ  lqð  r@ó  lAð  NO÷  NSñ  NSð  T_ó  N`ð  no÷  nsñ  nsð  t@ð  ACó  nDô  SW÷  S]ñ  S]ð  ^_÷  ^cñ  ^cð  dqð  rvó  ^wó  Sxñ  *yô  z÷ 	9ñ  	9ð Ð(Ò(ð HJ÷  HRñ  HRð  SUó  HVö  Wð  CD”dœ3ÒyÐz{Ó|Õ}ð  W�ð  WØÒu�A¬D¯J©J°q·u±u¸YÓ7GÓ,H˜q ™|ÔX\×XbÑXbÐcd×chÑchÐisÓctÓXuÈ!ÈJÊ-ÐuÜ˜U¨Ô.÷% 	9ñ  	9ð& �Ò&¨7¯;©;Ð7GÓ+HÈ&Ò+PÜ˜7Ÿ;™;Ð'7Ó8Ò=¸AÓ>�ÌÏ
É
ÐSZÐ[gÑSh×SmÑSmÐntÓSuÒS~Ðy~ÓHÀØ3ò �AØ˜G’| R§Z¡ZÐ0iÐklÔmpÐqxÐyzÑq{Óm|Ðj}Õ%~ðà'¨7Ñ2°B·J±JÐ?xð  {Oð  W^ð  _sò  Wtñ  PSð  z}ð  z~ô  5Ø—	‘	”¤E¨%°t¸D°kÔ$B÷1 	9ñ  	9ð2 Ð(Ó(¨W¯[©[Ð9IÓ-JÈFÓ-RÜ˜7Ÿ;™;Ð'7Ó8Ò=¸AÓ>�Ä$Ç*Á*ÈWÐUaÑMb×MgÑMgÐhnÓMoÒMxÐsxÓByÀØŸ™˜i¨Ó+�ñ  IPÔ2BÀ7ÌEÔR[Ð\^ÓR_Ð`kÑRlÓLmÐop×otÑotÐuð  ACó  pDô  3Eð  VZ¨TÙ&*�d—l’l°�Ù%)¨d¯mªm‘zÀ�Ø—
‘
ð  \ð  ^_÷  ^cñ  ^cð  dpð  qsó  ^tð  uv÷  uzñ  uzð  {Bð  CMó  uNð  OP÷  OTñ  OTð  U`ð  acó  Odð  ekð  lqô  rv÷  r|ñ  r|ð  }Dó  rEô  FJ÷  FPñ  FPð  QR÷  QVñ  QVð  Wað  bdó  Qeó  Ffô  gjô  ko÷  ktñ  któ  kvó  gwð  ]xô  yØ—	‘	”¤E¨%¸ÈÐV\ÐejÑBkÔ$l÷? 	9ñ  	9ð@ �sŠ{ 4§:¡:¨e°RÓ#8÷A 	9ñ  	9ð@ ÷A 	9ôB �U˜3 ¨Ð4Ó5Ð5ùó- @|ùò
 W÷! 	9ð  	9úsr   ²AV4ÂAV4Ã51V4Ä0V4ÅV4Å0V4ÆV)ÆV4Æ%B6V4É%V4É=V/ÊA#V4ÌA+V4Í4AV4ÏF
V4Õ1V4Ö)V4Ö4V=c                 ó  — t        |«      } |ddg«       t        d«      j                  «       j                  dt	        j
                  |d   «      «      j                  dt	        j
                  |d   «      «      j                  «       gS )Nr1   )r5   ztext/html; charset=utf-8z/root/workspace/shorts_ui.htmlÚ__THRESHOLD__r'   Ú
__SOURCE__rt   )r/   r   Ú	read_textÚreplaceÚhtmlÚescaper9   )rH   r<   r   r?   s       r   r—   z	App._page^   s²   € Ü�r‹]ˆ™E (Ð,WÐ+XÔYÜÐ5Ó6×@Ñ@ÓB×JÑJÈ?Ô[_×[fÑ[fÐgkÐlwÑgxÓ[yÓz÷  Cñ  Cð  DPô  QU÷  Q\ñ  Q\ð  ]að  bnñ  ]oó  Qpó  q÷  xñ  xó  zð  {ð  	{rA   c                 ó&   — | j                  ||«      S rE   )rb   )rH   r˜   r<   s      r   Ú__call__zApp.__call__a   s   € ¨T¯Z©Z¸ÀÓ-FÐ&FrA   N)rN   rO   rP   rI   rj   rb   r—   rª   r   rA   r   rf   rf   7   s   „ Ú@Ú2ò#6òH{ó GrA   rf   c                 ó.   — t        | «       t        | «      S rE   )r   rf   )r   s    r   Ú
create_appr¬   c   s   € œG BœK´°B³¨rA   u{  <!doctype html><html><head><meta name="viewport" content="width=device-width"><title>Shorts Control</title><style>body{font:15px system-ui;background:#101216;color:#eee;max-width:1100px;margin:32px auto;padding:0 20px}h1{color:#d1fe17}section{background:#191c22;border:1px solid #30343d;border-radius:12px;padding:18px;margin:16px 0}input,button{padding:10px;border-radius:7px;border:1px solid #555;background:#101216;color:#fff}button{background:#d1fe17;color:#111;font-weight:700;cursor:pointer}.muted{color:#9da3ae}.card{border-top:1px solid #363b45;padding:12px 0}.score{font-size:24px;color:#d1fe17}</style></head><body><h1>Shorts Control</h1><p class="muted">ECC-inspired scoring dashboard. Public publishing is disabled until platform OAuth and source rights are configured.</p><section><h2>Source and gate</h2><label>YouTube source URL<br><input id="source" size="65" value="__SOURCE__"></label><br><br><label>Publish threshold <input id="threshold" type="number" min="0" max="100" value="__THRESHOLD__"></label> <button onclick="save()">Save settings</button><p id="state"></p></section><section><h2>Pipeline status</h2><div id="summary">Loadingâ€¦</div></section><section><h2>Candidate clips</h2><div id="candidates">Loadingâ€¦</div></section><script>async function api(u,o){return (await fetch(u,o)).json()}async function save(){let p={source_url:source.value,threshold:Number(threshold.value),publishing_enabled:false};await api('/api/settings',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(p)});state.textContent='Saved. Public publishing remains disabled.';load()}async function load(){let s=await api('/api/summary');summary.innerHTML=`Threshold: <b>${s.threshold}</b> Â· Publishing: <b>${s.publishing_enabled?'ENABLED':'DISABLED'}</b> Â· Source: ${s.source_url||'not set'}<br>Counts: ${JSON.stringify(s.counts)}`;let c=await api('/api/candidates');candidates.innerHTML=c.length?c.map(x=>`<div class="card"><b>${x.title}</b><br><span class="score">${x.score?x.score.toFixed(1):'â€”'}</span> Â· ${x.status}<br><span class="muted">${x.source_url||''}</span></div>`).join(''):'No candidates yet.'}load()</script></body></html>Ú__main__z,shorts dashboard listening on 127.0.0.1:8790T)Úflushz	127.0.0.1iV"  )éU   N)r2   N)r§   r7   r   Ú	threadingr–   Úpathlibr   Úurllib.parser   r   Úwsgiref.simple_serverr   Úshorts_pipeliner   r   r	   ÚDB_PATHr   r*   r/   r@   rC   rR   rf   r¬   ÚPAGErN   rT   ÚprintÚserve_foreverr   rA   r   ú<module>r¹      s¬   ðã Û Û Û Û Ý ß +Ý -ß ?Ñ ?á
Ð6Ó
7€àó ó ]òIó÷
6ñ 6÷9ñ 9÷*Gñ *GðX Ó 7ð G"€àˆZÒÙ‹€C‘eÐJÐRVÕWÙ�˜D Ó%×3Ñ3Õ5ð rA   