§
   ýõŠ„“ú–ã                   ó„   — d Z ddlZddlZddlZddlZddlZddlZddlZ G d„ d¦  «        Zda	 ej
        ¦   «         Zd„ ZdS )zÅ
Unified Cache Manager for the Milionar addon.
Handles both metadata (JSON) and binary assets (images) with:
- In-memory caching for speed
- Atomic writes for safety
- Automatic garbage collection
é    Nc                   óœ   — e Zd ZdZdZdZdZdd„Zdd„Zdd„Z	d	„ Z
d
„ Zd„ Zd„ Zd„ Zd„ Zdd„Zdd„Zdd„Zdd„Zd„ Zed„ ¦   «         Zd„ ZdS )ÚCacheManagerz€
    Centralized cache manager for metadata and generated assets.
    Thread-safe with atomic writes and automatic cleanup.
    é   éd   zcache_metadata.jsonNc                 ó2  — |r|| _         nt          j        d¦  «        | _         t          j        | j         ¦  «        st          j        | j         ¦  «         i | _        d| _        t          j        ¦   «         | _	        |  
                    ¦   «          d| _        dS )zœ
        Initialize the cache manager.
        
        Args:
            cache_dir: Base directory for cache files. Defaults to addon_data folder.
        z9special://profile/addon_data/plugin.video.milionar/cache/Fr   N)Ú
_cache_dirÚxbmcvfsÚtranslatePathÚexistsÚmkdirsÚ	_metadataÚ_metadata_dirtyÚ	threadingÚLockÚ_metadata_lockÚ_load_metadataÚ_last_cleanup)ÚselfÚ	cache_dirs     úY/Users/chudy/Library/Application Support/Kodi/addons/plugin.video.milionar/wcs/caching.pyÚ__init__zCacheManager.__init__   s™   € ð ð 	Ø'ˆDŒOˆOå%Ô3ØKñô ˆDŒOõ
 Œ~˜dœoÑ.Ô.ð 	,ÝŒN˜4œ?Ñ+Ô+Ð+ð ˆŒØ$ˆÔÝ'œnÑ.Ô.ˆÔð 	×ÒÑÔÐð ˆÔÐÐó    c                 ó@  — | j         5  | j                             |¦  «        }|€|cddd¦  «         S d|v r:|d         t          j        ¦   «         k     r| j        |= d| _        |cddd¦  «         S |                     d|¦  «        cddd¦  «         S # 1 swxY w Y   dS )zå
        Get a value from the metadata cache.
        
        Args:
            key: Cache key string
            default: Default value if key not found
            
        Returns:
            Cached value or default
        NÚexpiresTÚdata)r   r   ÚgetÚtimer   )r   ÚkeyÚdefaultÚentrys       r   r   zCacheManager.get?   s(  € ð Ô ð 	.ð 	.Ø”N×&Ò& sÑ+Ô+ˆEØˆ}Øð	.ð 	.ð 	.ð 	.ñ 	.ô 	.ð 	.ð 	.ð ˜EÐ!Ð! e¨IÔ&6½¼¹¼Ò&DÐ&Dà”N 3Ð'Ø'+�Ô$Øð	.ð 	.ð 	.ð 	.ñ 	.ô 	.ð 	.ð 	.ð —9’9˜V WÑ-Ô-ð	.ð 	.ð 	.ð 	.ñ 	.ô 	.ð 	.ð 	.ð 	.ð 	.ð 	.ð 	.øøøð 	.ð 	.ð 	.ð 	.ð 	.ð 	.s   ˆB³1BÁ1BÂBÂBc                 óÊ   — | j         5  |t          j        ¦   «         dœ}|rt          j        ¦   «         |z   |d<   || j        |<   d| _        ddd¦  «         dS # 1 swxY w Y   dS )zã
        Set a value in the metadata cache.
        
        Args:
            key: Cache key string
            data: Data to cache (must be JSON serializable)
            ttl_seconds: Optional time-to-live in seconds
        )r   Ú	timestampr   TN)r   r   r   r   )r   r   r   Úttl_secondsr    s        r   ÚsetzCacheManager.setX   sµ   € ð Ô ð 	(ð 	(Ø!µ´	±´Ð<Ð<ˆEØð =Ý#'¤9¡;¤;°Ñ#<��iÑ Ø"'ˆDŒN˜3ÑØ#'ˆDÔ ð	(ð 	(ð 	(ñ 	(ô 	(ð 	(ð 	(ð 	(ð 	(ð 	(ð 	(ð 	(øøøð 	(ð 	(ð 	(ð 	(ð 	(ð 	(s   ˆAAÁAÁAc                 óv   — | j         5  || j        v r| j        |= d| _        ddd¦  «         dS # 1 swxY w Y   dS )z!Remove a key from metadata cache.TN©r   r   r   )r   r   s     r   ÚdeletezCacheManager.deleteh   sŽ   € àÔ ð 	,ð 	,Ø�d”nÐ$Ð$Ø”N 3Ð'Ø'+�Ô$ð	,ð 	,ð 	,ñ 	,ô 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,øøøð 	,ð 	,ð 	,ð 	,ð 	,ð 	,s   ˆ.®2µ2c                 óš   ‡— | j         5  ˆfd„| j        D ¦   «         }|D ]
}| j        |= Œ|rd| _        ddd¦  «         dS # 1 swxY w Y   dS )z'Remove all keys starting with a prefix.c                 ó>   •— g | ]}|                      ‰¦  «        ¯|‘ŒS © )Ú
startswith)Ú.0ÚkÚprefixs     €r   ú
<listcomp>z.CacheManager.delete_prefix.<locals>.<listcomp>r   s*   ø€ ÐPÐPÐP A¸1¿<º<ÈÑ;OÔ;OÐP˜aÐPÐPÐPr   TNr&   )r   r.   Úkeys_to_deleter   s    `  r   Údelete_prefixzCacheManager.delete_prefixo   s·   ø€ àÔ ð 	,ð 	,ØPÐPÐPÐP¨¬ÐPÑPÔPˆNØ%ð (ð (�Ø”N 3Ð'Ð'Øð ,Ø'+�Ô$ð	,ð 	,ð 	,ñ 	,ô 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,øøøð 	,ð 	,ð 	,ð 	,ð 	,ð 	,s   ‰*A Á AÁAc                 ó˜   — | j         5  | j        s	 ddd¦  «         dS |                      ¦   «          ddd¦  «         dS # 1 swxY w Y   dS )z+Force save metadata to disk (atomic write).N)r   r   Ú_save_metadata_locked)r   s    r   ÚflushzCacheManager.flushx   s¹   € àÔ ð 	)ð 	)ØÔ'ð Øð	)ð 	)ð 	)ñ 	)ô 	)ð 	)ð 	)ð 	)ð ×&Ò&Ñ(Ô(Ð(ð	)ð 	)ð 	)ñ 	)ô 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)øøøð 	)ð 	)ð 	)ð 	)ð 	)ð 	)s   ˆ	?ž?¿AÁAc                 ó(  — t           j                             | j        | j        ¦  «        }	 t          j        |¦  «        r�t          j        |d¦  «        }|                     ¦   «         }| 	                    ¦   «          |rRt          j        |¦  «        | _        t          j        dt          | j        ¦  «        › d�t          j        ¦  «         dS dS dS # t"          $ r4}t          j        d|› �t          j        ¦  «         i | _        Y d}~dS d}~ww xY w)z$Load metadata from disk into memory.Úrz[CacheManager] Loaded z cached entriesz'[CacheManager] Error loading metadata: N)ÚosÚpathÚjoinr   ÚMETADATA_FILENAMEr	   r   ÚFileÚreadÚcloseÚjsonÚloadsr   ÚxbmcÚlogÚlenÚLOGINFOÚ	ExceptionÚ
LOGWARNING)r   Úmetadata_pathÚfÚcontentÚes        r   r   zCacheManager._load_metadata   s  € åœŸš T¤_°dÔ6LÑMÔMˆð
	 ÝŒ~˜mÑ,Ô,ð jÝ”L °Ñ4Ô4�ØŸ&š&™(œ(�Ø—’‘	”	�	Øð jÝ%)¤Z°Ñ%8Ô%8�D”NÝ”HÐZµc¸$¼.Ñ6IÔ6IÐZÐZÐZÕ\`Ô\hÑiÔiÐiÐiÐiðjð jðjð jøõ ð 	 ð 	 ð 	 ÝŒHÐB¸qÐBÐBÅDÄOÑTÔTÐTØˆDŒNˆNˆNˆNˆNˆNˆNøøøøð	 øøøs   ¬B!C Ã
DÃ)DÄDc                 óp  — t           j                             | j        | j        ¦  «        }|dz   }	 t          j        | j        d¬¦  «        }t          j	        |d¦  «        }| 
                    |¦  «         |                     ¦   «          t          j        |¦  «        rt          j        |¦  «         t          j        ||¦  «         d| _        dS # t           $ r[}t#          j        d|› �t"          j        ¦  «         t          j        |¦  «        rt          j        |¦  «         Y d}~dS Y d}~dS d}~ww xY w)zŠ
        Save metadata to disk with atomic write (must hold lock).
        Uses temp file + rename pattern to prevent corruption.
        ú.tmpF)Úensure_asciiÚwz&[CacheManager] Error saving metadata: N)r7   r8   r9   r   r:   r>   Údumpsr   r	   r;   Úwriter=   r   r'   Úrenamer   rD   r@   rA   ÚLOGERROR)r   rF   Ú	temp_pathrH   rG   rI   s         r   r3   z"CacheManager._save_metadata_lockedŽ   sE  € õ
 œŸš T¤_°dÔ6LÑMÔMˆØ! FÑ*ˆ	ð	*å”j ¤¸eÐDÑDÔDˆGÝ”˜Y¨Ñ,Ô,ˆAØ�GŠG�GÑÔÐØ�GŠG‰IŒIˆIõ Œ~˜mÑ,Ô,ð .Ý”˜}Ñ-Ô-Ð-ÝŒN˜9 mÑ4Ô4Ð4à#(ˆDÔ Ð Ð øÝð 	*ð 	*ð 	*ÝŒHÐA¸aÐAÐAÅ4Ä=ÑQÔQÐQåŒ~˜iÑ(Ô(ð *Ý”˜yÑ)Ô)Ð)Ð)Ð)Ð)Ð)Ð)Ð)ð*ð *ð *ð *ð *ð *øøøøð	*øøøs   ±BC Ã
D5ÃA
D0Ä0D5c                 ó¦   — |                       |¦  «        }t          j                             | j        |¦  «        }t          j        |¦  «        r|S dS )zí
        Get the filesystem path for a cached asset.
        
        Args:
            cache_key: Unique identifier for the asset
            
        Returns:
            Absolute path to the cached file, or None if not cached
        N)Ú_asset_filenamer7   r8   r9   r   r	   r   )r   Ú	cache_keyÚfilenameÚfilepaths       r   Úget_asset_pathzCacheManager.get_asset_path­   sJ   € ð ×'Ò'¨	Ñ2Ô2ˆÝ”7—<’< ¤°Ñ:Ô:ˆåŒ>˜(Ñ#Ô#ð 	ØˆOØˆtr   ú.jpgc                 óH  — |                       ||¦  «        }t          j                             | j        |¦  «        }|dz   }	 t          j        |d¦  «        }|                     |¦  «         |                     ¦   «          t          j	        |¦  «        rt          j
        |¦  «         t          j        ||¦  «         |S # t          $ rX}t          j        d|› d|› �t          j        ¦  «         t          j	        |¦  «        rt          j
        |¦  «         Y d}~dS d}~ww xY w)a(  
        Save binary asset to cache.
        
        Args:
            cache_key: Unique identifier for the asset
            data: Binary data (bytes) to save
            extension: File extension (default .jpg)
            
        Returns:
            Absolute path to the saved file
        rK   Úwbz"[CacheManager] Error saving asset ú: N)rT   r7   r8   r9   r   r	   r;   rO   r=   r   r'   rP   rD   r@   rA   rQ   )	r   rU   r   Ú	extensionrV   rW   rR   rG   rI   s	            r   Ú
save_assetzCacheManager.save_asset¾   s  € ð ×'Ò'¨	°9Ñ=Ô=ˆÝ”7—<’< ¤°Ñ:Ô:ˆØ˜vÑ%ˆ	ð	å”˜Y¨Ñ-Ô-ˆAØ�GŠG�D‰MŒMˆMØ�GŠG‰IŒIˆIõ Œ~˜hÑ'Ô'ð )Ý”˜xÑ(Ô(Ð(ÝŒN˜9 hÑ/Ô/Ð/àˆOøÝð 	ð 	ð 	ÝŒHÐJ¸)ÐJÐJÀqÐJÐJÍDÌMÑZÔZÐZÝŒ~˜iÑ(Ô(ð *Ý”˜yÑ)Ô)Ð)Ø�4�4�4�4�4øøøøð		øøøs   ÁA<B? Â?
D!Ã	ADÄD!éU   c                 ó$  — ddl m} 	  |¦   «         }|                     |d|¬¦  «         |                     ¦   «         }|                      ||d¦  «        S # t
          $ r0}t          j        d|› d|› �t          j        ¦  «         Y d}~dS d}~ww xY w)	a7  
        Save a PIL Image object to cache as JPEG.
        
        Args:
            cache_key: Unique identifier for the asset
            pil_image: PIL Image object
            quality: JPEG quality (1-100)
            
        Returns:
            Absolute path to the saved file, or None on error
        r   )ÚBytesIOÚJPEG)ÚformatÚqualityrY   z&[CacheManager] Error saving PIL image r\   N)	Úiora   ÚsaveÚgetvaluer^   rD   r@   rA   rQ   )r   rU   Ú	pil_imagerd   ra   Úbufferr   rI   s           r   Úsave_pil_imagezCacheManager.save_pil_imageà   s¶   € ð 	ÐÐÐÐÐð	Ø�W‘Y”YˆFØ�NŠN˜6¨&¸'ˆNÑBÔBÐBØ—?’?Ñ$Ô$ˆDØ—?’? 9¨d°FÑ;Ô;Ð;øÝð 	ð 	ð 	ÝŒHÐN¸iÐNÐNÈ1ÐNÐNÕPTÔP]Ñ^Ô^Ð^Ø�4�4�4�4�4øøøøð	øøøs   ˆAA Á
BÁ%B
Â
Bc                 ó°   — t          |t          ¦  «        r|                     d¦  «        }t          j        |¦  «                             ¦   «         }d|› |› �S )z7Generate a safe filename from cache key using MD5 hash.zutf-8Úasset_)Ú
isinstanceÚstrÚencodeÚhashlibÚmd5Ú	hexdigest)r   rU   r]   Úhash_strs       r   rT   zCacheManager._asset_filename÷   sU   € å�i¥Ñ%Ô%ð 	2Ø!×(Ò(¨Ñ1Ô1ˆIÝ”;˜yÑ)Ô)×3Ò3Ñ5Ô5ˆØ-˜Ð- )Ð-Ð-Ð-r   Fc                 óü   — |s!t          j         ¦   «         | j        z
  dk     rdS t          j         ¦   «         | _        t          j        | j        |p| j        |p| j        fd¬¦  «        }|                     ¦   «          dS )aM  
        Run cache maintenance (cleanup old files).
        Safe to call frequently - will skip if recently run.
        
        Args:
            max_age_days: Delete files older than this (default 7)
            max_size_mb: Target maximum cache size (default 100MB)
            force: Force run even if recently executed
        é  NT)ÚtargetÚargsÚdaemon)r   r   r   ÚThreadÚ_maintenance_workerÚDEFAULT_MAX_AGE_DAYSÚDEFAULT_MAX_SIZE_MBÚstart)r   Úmax_age_daysÚmax_size_mbÚforceÚthreads        r   ÚmaintenancezCacheManager.maintenance  s‡   € ð ð 	�$œ)™+œ+¨Ô(:Ñ:¸dÒBÐBØˆFå!œY™[œ[ˆÔõ Ô!ØÔ+ØÐ; $Ô";ØÐ9 Ô!9ð;àð	
ñ 
ô 
ˆð 	�Š‰Œˆˆˆr   c                 ób  — 	 t          j         ¦   «         }|dz  dz  }|dz  dz  }| j        5  g }| j                             ¦   «         D ]U\  }}d|v r"|d         |k     r|                     |¦  «         Œ+d|v r&||d         z
  }	|	|k    r|                     |¦  «         ŒV|D ]
}| j        |= Œ|r7d| _        t          j        dt          |¦  «        › d�t          j	        ¦  «         d	d	d	¦  «         n# 1 swxY w Y   g }
d
}d
}t          j        | j        ¦  «        \  }}|D ]Ì}|| j        k    s|                     d¦  «        rŒ#t          j                             | j        |¦  «        }	 t          j        |¦  «        }|                     ¦   «         }|                     ¦   «         }||z
  |k    rt          j        |¦  «         |dz  }n|
                     |||f¦  «         ||z  }ŒÆ#  Y ŒÊxY w||k    rF|
                     d„ ¬¦  «         |
D ],\  }}}||k    r nt          j        |¦  «         ||z  }|dz  }Œ-|d
k    r#t          j        d|› d�t          j	        ¦  «         |                      ¦   «          d	S # t0          $ r-}t          j        d|› �t          j        ¦  «         Y d	}~d	S d	}~ww xY w)z$Background worker for cache cleanup.é   ru   i   r   r"   Tz[CacheManager] Cleaned z expired metadata entriesNr   rK   é   c                 ó   — | d         S )Né   r*   )Úxs    r   ú<lambda>z2CacheManager._maintenance_worker.<locals>.<lambda>Q  s
   € ¨a°¬d€ r   )r   z[CacheManager] Deleted z old cache filesz"[CacheManager] Maintenance error: )r   r   r   ÚitemsÚappendr   r@   rA   rB   rC   r	   Úlistdirr   r:   Úendswithr7   r8   r9   ÚStatÚst_sizeÚst_mtimer'   Úsortr4   rD   rQ   )r   r~   r   ÚnowÚmax_age_secondsÚmax_size_bytesÚexpired_keysr   r    ÚageÚ
files_infoÚ
total_sizeÚdeleted_countÚdirsÚfilesrV   rW   ÚstatÚsizeÚmtimerI   s                        r   rz   z CacheManager._maintenance_worker  s‹  € ðD	NÝ”)‘+”+ˆCØ*¨RÑ/°$Ñ6ˆOØ(¨4Ñ/°$Ñ6ˆNð Ô$ð sð sØ!�Ø"&¤.×"6Ò"6Ñ"8Ô"8ð 5ð 5‘J�C˜Ø  EÐ)Ð)¨e°IÔ.>ÀÒ.DÐ.DØ$×+Ò+¨CÑ0Ô0Ð0Ð0Ø$¨Ð-Ð-Ø! E¨+Ô$6Ñ6˜Ø Ò0Ð0Ø(×/Ò/°Ñ4Ô4Ð4øà'ð ,ð ,�CØœ sÐ+Ð+àð sØ+/�DÔ(Ý”HÐcµs¸<Ñ7HÔ7HÐcÐcÐcÕeiÔeqÑrÔrÐrðsð sð sñ sô sð sð sð sð sð sð søøøð sð sð sð sð$ ˆJØˆJØˆMå!œ/¨$¬/Ñ:Ô:‰KˆD�%Ø!ð ð �Ø˜tÔ5Ò5Ð5¸×9JÒ9JÈ6Ñ9RÔ9RÐ5Øåœ7Ÿ<š<¨¬¸ÑBÔB�ðÝ"œ<¨Ñ1Ô1�DØŸ<š<™>œ>�DØ ŸMšM™OœO�Eð ˜e™ Ò6Ð6Ýœ xÑ0Ô0Ð0Ø%¨Ñ*˜˜à"×)Ò)¨8°T¸5Ð*AÑBÔBÐBØ" dÑ*˜
øøðØ�Døøøð ˜NÒ*Ð*à—’ N N�Ñ3Ô3Ð3à-7ð 'ð 'Ñ)�H˜d EØ! ^Ò3Ð3Ø˜Ý”N 8Ñ,Ô,Ð,Ø $Ñ&�JØ! QÑ&�M�Mà˜qÒ Ð Ý”ÐR°=ÐRÐRÐRÕTXÔT`ÑaÔaÐað �JŠJ‰LŒLˆLˆLˆLøåð 	Nð 	Nð 	NÝŒHÐ=¸!Ð=Ð=½t¼}ÑMÔMÐMÐMÐMÐMÐMÐMÐMøøøøð	NøøøsV   ‚*I7 ¬B8C0Ã$I7 Ã0C4Ã4I7 Ã7C4Ã8A/I7 Å(A<G%Ç$I7 Ç%G)Ç'BI7 É7
J.Ê"J)Ê)J.c                  ó@   — d                      d„ | D ¦   «         ¦  «        S )z›
        Create a cache key from multiple arguments.
        
        Example:
            key = CacheManager.make_key('program_list', channel_id)
        Ú_c              3   ó4   K  — | ]}t          |¦  «        V — Œd S ©N)rn   )r,   Úargs     r   ú	<genexpr>z(CacheManager.make_key.<locals>.<genexpr>o  s(   è è € Ð1Ð1 S�˜C™œÐ1Ð1Ð1Ð1Ð1Ð1r   )r9   )rw   s    r   Úmake_keyzCacheManager.make_keyg  s%   € ð �xŠxÐ1Ð1¨DÐ1Ñ1Ô1Ñ1Ô1Ð1r   c                 óR  — | j         5  i | _        d| _        ddd¦  «         n# 1 swxY w Y   t          j        | j        ¦  «        \  }}|D ];}t          j                             | j        |¦  «        }t          j	        |¦  «         Œ<t          j        dt          j        ¦  «         dS )z+Clear all cached data (metadata and files).TNz[CacheManager] Cache cleared)r   r   r   r	   rŒ   r   r7   r8   r9   r'   r@   rA   rC   )r   rš   r›   rV   rW   s        r   Ú	clear_allzCacheManager.clear_allq  sÜ   € ð Ô ð 	(ð 	(ØˆDŒNØ#'ˆDÔ ð	(ð 	(ð 	(ñ 	(ô 	(ð 	(ð 	(ð 	(ð 	(ð 	(ð 	(øøøð 	(ð 	(ð 	(ð 	(õ
 ”o d¤oÑ6Ô6‰ˆˆeØð 	%ð 	%ˆHÝ”w—|’| D¤O°XÑ>Ô>ˆHÝŒN˜8Ñ$Ô$Ð$Ð$åŒÐ/µ´Ñ>Ô>Ð>Ð>Ð>s   ˆ#£'ª'r¢   )rY   )r_   )NNF)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r{   r|   r:   r   r   r$   r'   r1   r4   r   r3   rX   r^   rj   rT   r‚   rz   Ústaticmethodr¥   r§   r*   r   r   r   r      sZ  € € € € € ðð ð ÐØÐØ-Ððð ð ð ðB.ð .ð .ð .ð2(ð (ð (ð (ð ,ð ,ð ,ð,ð ,ð ,ð)ð )ð )ð ð  ð  ð*ð *ð *ð>ð ð ð" ð  ð  ð  ðDð ð ð ð..ð .ð .ð .ðð ð ð ð2FNð FNð FNðX ð2ð 2ñ „\ð2ð?ð ?ð ?ð ?ð ?r   r   c                  ó†   — t           €4t          5  t           €t          ¦   «         a ddd¦  «         n# 1 swxY w Y   t           S )zk
    Get the global CacheManager instance (singleton).
    
    Returns:
        CacheManager instance
    N)Ú	_instanceÚ_instance_lockr   r*   r   r   Úget_cache_managerr°   †  s|   € õ ÐÝð 	+ð 	+ÝÐ Ý(™NœN�	ð	+ð 	+ð 	+ñ 	+ô 	+ð 	+ð 	+ð 	+ð 	+ð 	+ð 	+øøøð 	+ð 	+ð 	+ð 	+õ Ðs   �1±5¸5)r«   r7   r>   r   rp   r   r@   r	   r   r®   r   r¯   r°   r*   r   r   ú<module>r±      s³   ððð ð 
€	€	€	Ø €€€Ø €€€Ø €€€Ø Ð Ð Ð Ø €€€Ø €€€ðk?ð k?ð k?ð k?ð k?ñ k?ô k?ð k?ð^ €	Ø�”Ñ!Ô!€ðð ð ð ð r   