Files
caddybuddy/__pycache__/app.cpython-313.pyc
T

119 lines
25 KiB
Plaintext
Raw Normal View History

2026-04-18 07:15:39 +12:00
ó
âi§LãóSrSSKrSSKrSSKrSSKrSSKrSSKrSSKrSSKJ r J
r
SSK J r J r SSK
JrJrJr SSKJrJrJr \R*R-SS5r\R*R-S S
5r\R*R-S S 5r\"\5rS
r\R:r\R>"S\5r \R>"S\5r!\R>"S\5r"\R>"S\5r#\R>"S\5r$Sr%Sr&Sr'Sr(/SQr)Sr*Sr+Sr,Sr-S+Sjr.Sr/Sr0Sr1S r2S!r3\RiS"5S#5r5\RiS$5S%5r6\S&:Xa\RoS'S(S)S*9 gg),u
CaddyBuddy — Caddy Log Dashboard
Reads a Caddy JSON access log and surfaces useful insights.
Run: python app.py
Open: http://127.0.0.1:5000
Optional Emby API integration (resolves device IDs to usernames):
EMBY_URL=http://localhost:8096 EMBY_KEY=<admin_api_key> python app.py
éN)ÚCounterÚ defaultdict)ÚdatetimeÚtimezone)Ú unquote_plusÚparse_qsÚurlparse)ÚFlaskÚrender_templateÚjsonifyÚ CADDY_LOGz log2.jsonÚEMBY_URLzhttp://10.0.0.2:8096ÚEMBY_KEYÚ b9af54b630f6448289ab96422add567acóšU(dgURU5n[U[5(a[SU5S5$U=(d S$)zFCaddy stores header values as lists; return the first non-empty value.Nc3ó6# UHo(dM Uv M g7f©)Ú.0Úvs Ú'C:\Users\Matt\Downloads\files(5)\app.pyÚ <genexpr>Ú_first.<locals>.<genexpr>,séИ1«—Q‘Q¢ùs
 )ÚgetÚ
isinstanceÚlistÚnext)ÚheadersÚkeyÚvalss rÚ_firstr!&s@æ ØØ ;‰; €DÜ×ÑÜÑÓ*¨DÓ <ózClient="?([^,"\n]+)"?zVersion="?([0-9][^,"\s]*)"?z!Device(?!Id)"?[=\s]"?([^,"\n]+)"?zDeviceId"?[=\s]"?([^,"\s&]+)"?z"Token"?[=\s]"?([a-fA-F0-9]{24,})"?cóTU(dgSnU"[RU55nU"[RU55nU"[RU55nU"[RU55nU"[
RU55nU(a [
U5nX#XEU4$)zl
Parse a MediaBrowser/Emby authorization header value.
Returns (client, version, device, device_id, token).
©NNNNNcóRU(aURS5R5$S$))ÚgroupÚstrip)Úms rÚ_gÚ_parse_auth_header.<locals>._gBs Þ%&ˆqw‰wqz×ÑÓ0¨DÐ0r")ÚEMBY_CLIENT_REÚsearchÚEMBY_VERSION_REÚEMBY_DEVICE_REÚEMBY_DEVICE_ID_REÚ
EMBY_TOKEN_REr)Úvalr*ÚclientÚversionÚdeviceÚ device_idÚtokens rÚ_parse_auth_headerr8:s—ö
Ø”>×Ó.€FÙ”?×)¨#Ó/€GÙ”>×Ó.€FÙÔ+¨CÓ1€IÙ”=×Ó-€EöܘfÓ%ˆà ˜F¨uÐ 4r"cóîU(aSU;a0$[[U5RSS9nUR5VVs0sHup#U(dMX#S_M snn$s snnf![a 0s$f=f)z@Return a flat {key: first_value} dict from a URI's query string.Ú?F)Úkeep_blank_valuesr)rr ÚqueryÚitemsÚ Exception)ÚuriÚqsÚkrs rÚparse_query_stringrBRsiæ #˜S“.؈ ðÜ
”h˜s“m×)¸
CˆØ$&§H¡H¤JÔ4¢J™D˜A´!“Q‘4’¡JÒ4ùÓ4øÜ óØŠ ðús)0A%Á
AÁ AÁA%ÁA%Á% A4Á3A4có:URS05=(d 0nURS05=(d 0nURSS5n[US5n[U5upVpxn U(d [US5nU(d [US5nU(d [US5n
U
(a [U
5OS nU(d [US
5nU (d[US 5=(d [US 5n [ XVXx/5(d‰[ U5n U(dSU ;a[U S5nU(dU RS5nU(dSU ;a[U S5nU(dU RS
5nU (dU RS 5n [ XVXx/5(dg
XVXxU 4$)
Best-effort identification of an Emby client from a log entry.
Returns (client, version, device, device_id, token).
All fields may be None if this is not an Emby request.
Úrequestrr?ÚzX-Emby-Authorizationú
X-Emby-ClientúX-Emby-Client-VersionzX-Emby-Device-NameNúX-Emby-Device-Idz X-Emby-TokenzX-MediaBrowser-Tokenr$)rr!r8rÚanyrB) ÚentryÚreqrr?Úauth_valr3r4r5r6r7Úrawr@s rÚ
classify_embyrN]snð i‰i˜  &×,¨"€CØg‰gi Ó$×€GØg‰ge˜RÓ €Cô6€HÜ0BÀ8Ó0LÑ-€Fö ܘ ÓÞ Ü˜Ð"9ÓÞ Ü3ˆÞ&)”˜"¨tˆÞ ܘ7Ð$6Ó7ˆ Þ Üw Ó/×Z´6¸'ÐCYÓ3Zˆô  Ð
 Ó
$ˆÞ˜/¨RÓ! " _Ñ"5Ó6ˆØ—f5ˆÐ.°"Ó! "Ð%9Ñ":Ó;ˆØŸÐ2ˆIÞØ—F‘F˜>Ó*ˆEä  Ð  ˜F¨uÐ 4r"cóŽ[(a [(d0$SSKn[RS5S[3nURR USS0S9nURR
USS 9n[R"UR55nSSS5 0nWRS
/5HknURS 5=(d URS 5nURS
5=(d URS5nU(dM^U(dMgX…U'Mm U$!,(df  N“=f![a 0s$f=f)
Call the Emby /Devices endpoint and return {device_id: last_user_name}.
Returns an empty dict if the API is not configured or unreachable.
r/z/emby/Devices?api_key=ÚAcceptzapplication/json)ré)ÚtimeoutÚItemsÚIdÚDeviceIdÚ LastUserNameÚUserName) rrÚurllib.requestÚrstriprDÚRequestÚurlopenÚjsonÚloadsÚreadrr>) ÚurllibÚurlrKÚrespÚdataÚresultÚitemÚdidÚusers rÚfetch_emby_device_usersrhs ÷
Š8Ÿ8š8؈ ðÛÜ Ó&Ð&<¼X¸GˆØn‰n×$ S°8Ð=OÐ2PÐQˆØ
^‰^×
# Ð
3°tÜ—:’:˜dŸi™i›kÓ*ˆD÷ˆØ—H‘H˜W bÖ)ˆ—88˜D“>×9 T§X¡X¨jÓ%9ˆ—88˜+×C¨t¯x©x¸
Ó/Cˆˆs—t"s“ ñ 
ˆ
÷
3ûô óØŠ ðús7šAD5Á2%D$ÂA6D5ÄD5Ä D5Ä$
D2Ä.D5Ä5 EÅE)z*.tar.gzz*.tgzz *.json.gzz*.log.gzz*.gzz*.jsonz*.logc
ó[RRU5(aU/$[RRU5(d/$[ 5n[
H‰nUR
[R"[RRX555 UR
[R"[RRUSU555 M [U[RRS9$)
Given a file path or a directory, return a sorted list of log file paths.
Files are sorted oldest-first by modification time so merged data is in
chronological order.
Ú*)r) ÚosÚpathÚisfileÚisdirÚsetÚ
_LOG_GLOBSÚupdateÚglobÚjoinÚsortedÚgetmtime)rlÚfoundÚpatterns rÚfind_log_filesrx±ô 
‡ww‡~~ÑØˆvˆ
Ü
7‰7=‰=˜× Ñ Øˆ ä ‹E€E߈Ø
”T—Y’YœrŸw™wŸ|™|¨DÓ
”T—Y’YœrŸw™wŸ|™|¨D°#°wÓô
%œRŸW™W× .r"c#ó|# [RRU5R5nUR S5(dUR S5([
R "US5nUR5HnUR5(dMURU5nUcM0UR5nUSSS:Xa[R"U5n[R"[R"U5SSS 9ShvN M“ SSS5 gUR S
5(a+[R "US SSS 9nUShvN SSS5 g[
US SSS 9nUShvN SSS5 gNr!,(df  g=fN@!,(df  g=fN3!,(df  g=f7f)
z“
Yield raw text lines from a log file regardless of compression format.
Handles: plain text, .gz, .tar.gz / .tgz (any members inside the archive).
ú.tar.gzú.tgzzr:gzNészutf-8Úreplace)ÚencodingÚerrorsú.gzÚrtÚr)rkrlÚbasenameÚlowerÚendswithÚtarfileÚopenÚ
getmembersrmÚ extractfiler_ÚgzipÚ
decompressÚioÚ
TextIOWrapperÚBytesIO)ÚfilepathÚnameÚtarÚmemberÚfobjrMÚfs rÚ _iter_linesr•ÅsYéô
7‰7× Ñ ˜HÓ -€Dà ‡}}Y×Ñ 4§=¡=°×#8Ñ#8Ü
\Š\˜( 
+¨sØŸ.™.Ö*Ø—}‘}—ÙØ vÓ.Ø‘<Ùà—ii“kØ7˜kÓŸ/š/¨#Ó.×+¬B¯JªJ°s«OÀgÐV_Ñ`×

u× Ñ Ü
YŠYx °À Ò
JÈaØLˆKÐ
(˜C¨'¸
ØLˆEÐ
a÷
+úñ
÷KÕ
Júñ
÷EÕ
Düs…A1F<Á3BFÄFÄFÄ6F<ÅFÅFÅFÅF<Å/F+Å5F)Å6F+Å: F<ÆFÆ
FÆF<ÆFÆ
F&Æ"F<Æ)F+Æ+
F9Æ5F<có”UR5nU(a USS:wag[R"U5nUR S05=(d 0nUR SS5nUR S05=(d 0n[ US5=(d Sn[
U5upgp‰n
UR S 5UUR S
S5UR S S5UR S S5UR S
S5UR SS5UR SS5UUUUU U
[ US5S.$![Ra gf=f)z;Parse one JSON log line into an entry dict, or return None.rÚ{NrDÚhostrErz
User-AgentÚtsÚmethodr?ÚstatusÚsizeÚdurationÚ remote_ipÚReferer)r™r˜r?rrÚ
user_agentÚ emby_clientÚ emby_versionÚ emby_deviceÚemby_device_idÚ
emby_tokenÚreferer)r(r]r^ÚJSONDecodeErrorrr!rN) rMÚrecrKr˜Úuar3r4r5r6r7s rÚ _parse_liner«ás1à
)‰)+€CÞ #a&˜C“-ØðÜjŠj˜‹oˆð 7‰79˜ !× ' R€CØ 7‰76˜ €DØ 7‰79˜ !× ' R€AÜ ! "× ( b€Bä0=¸cÓ0BÑ-€FðŸ'™' $ØŸ'™' (¨BÓŸ'™' ÓŸ'™' (¨AÓŸ'™' &¨!ÓŸ'™' *¨aÓŸ'™' +¨rÓØ Ø ØÜ   IÓ ðøô × Ñ óÙðús£D0Ä0EÅEcóð[U5n/n[U5SSS[5S.nUGHnUS==[RR U5-
ss'[RR
U5R5nURS5(dURS5(aSnOURS5(aSnOS nUS
U==S -
ss'[U5n[U5H(n[U5n U (dMURU 5 M* [U5U:”aUS ==S -
ss'GMGM [US
5US
'X#4$![a US