Compare commits

...

11 Commits

Author SHA1 Message Date
Benexl
f99ff546d5 feat(plugins): init examples 2025-08-18 16:20:48 +03:00
Benexl
88b707e060 feat: plugins system 2025-08-18 16:02:37 +03:00
Benexl
eaedf3268d feat(config): switch to toml format 2025-08-18 14:06:31 +03:00
Benexl
ade0465ea4 chore: set py version for pyright 2025-08-18 13:24:50 +03:00
Benexl
5e82db4ea8 chore: add repomixignore 2025-08-18 13:23:48 +03:00
Benexl
a10e56cb6f refactor:set min supported python version to 3.11 2025-08-18 13:19:56 +03:00
Benexl
fbd95e1966 feat(config-loader): allow env vars 2025-08-18 13:04:00 +03:00
Benexl
d37a441ccf fix(state): check for is None instead 2025-08-18 12:33:15 +03:00
Benexl
cbc1ceccbb feat(cli): auto check for updates 2025-08-18 02:14:56 +03:00
Benexl
249a207cad fix(update-command): use viu-media when updating 2025-08-18 01:28:59 +03:00
Benexl
c8a42c4920 Update README.md 2025-08-18 01:16:47 +03:00
47 changed files with 2399 additions and 489 deletions

View File

@@ -13,7 +13,7 @@ jobs:
strategy:
matrix:
python-version: ["3.10", "3.11"] # List the Python versions you want to test
python-version: ["3.11", "3.12"]
steps:
- uses: actions/checkout@v4

1
.repomixignore Normal file
View File

@@ -0,0 +1 @@
**/generated/**/*

319
PLUGINS.md Normal file
View File

@@ -0,0 +1,319 @@
# Viu Plugin Development Guide
This guide explains how to create plugins for viu, the terminal-based anime streaming tool.
## Overview
Viu supports four types of plugins:
- **Providers**: Add support for new anime streaming websites
- **Players**: Add support for new media players
- **Selectors**: Add support for new interactive selection tools
- **Commands**: Add new CLI commands to viu
## Plugin Structure
Every plugin must be a Git repository with the following structure:
```
your-plugin-repo/
├── plugin.info.toml # Plugin metadata (required)
├── your_module.py # Your plugin implementation
├── config.toml # Default configuration (optional)
├── requirements.txt # Dependencies (optional)
├── utils.py # Additional modules (optional)
├── helpers/ # Subdirectories supported (optional)
│ ├── __init__.py
│ └── parser.py
└── README.md # Documentation (recommended)
```
### Multi-File Plugins
Viu supports plugins with multiple Python files. You can organize your plugin code across multiple modules and import between them normally:
```python
# In your main plugin file
from utils import helper_function
from helpers.parser import ResponseParser
class MyProvider(BaseAnimeProvider):
def __init__(self, client, **config):
self.parser = ResponseParser()
# ... rest of implementation
```
The plugin system automatically adds your plugin directory to Python's import path during loading, so relative imports work as expected.
### Plugin Manifest (`plugin.info.toml`)
Every plugin repository must contain a `plugin.info.toml` file at its root:
```toml
[plugin]
name = "My Awesome Plugin"
version = "1.0.0"
description = "Adds support for Example Anime Site"
author = "Your Name"
homepage = "https://github.com/yourname/viu-example-plugin"
requires_python = ">=3.11"
[components]
# Specify which components your plugin provides
provider = "example_provider:ExampleProvider" # format: module:class
# player = "my_player:MyPlayer" # (if providing a player)
# selector = "my_selector:MySelector" # (if providing a selector)
# command = "my_command:my_command_func" # (if providing a command)
```
## Provider Plugins
Provider plugins add support for new anime streaming websites.
### Requirements
Your provider class must inherit from `BaseAnimeProvider` and implement:
- `search(query: str) -> SearchResults`
- `get(anime_id: str) -> Anime`
- `episode_streams(anime_id: str, episode: str) -> List[Server]`
### Example Provider Plugin
**plugin.info.toml:**
```toml
[plugin]
name = "Example Anime Provider"
version = "1.0.0"
description = "Adds support for example.anime.site"
[components]
provider = "example_provider:ExampleProvider"
```
**example_provider.py:**
```python
from typing import List
from httpx import Client
# These imports work because viu adds the plugin path to sys.path
from viu_media.libs.provider.anime.base import BaseAnimeProvider
from viu_media.libs.provider.anime.types import SearchResults, Anime, Server
class ExampleProvider(BaseAnimeProvider):
HEADERS = {
"Referer": "https://example.anime.site/",
}
def __init__(self, client: Client, **config):
self.client = client
# Access plugin configuration
self.timeout = config.get("timeout", 30)
self.preferred_quality = config.get("preferred_quality", "720p")
def search(self, query: str) -> SearchResults:
# Implement search logic
response = self.client.get(f"https://example.anime.site/search?q={query}")
# Parse response and return SearchResults
return SearchResults(...)
def get(self, anime_id: str) -> Anime:
# Implement anime details fetching
response = self.client.get(f"https://example.anime.site/anime/{anime_id}")
# Parse response and return Anime
return Anime(...)
def episode_streams(self, anime_id: str, episode: str) -> List[Server]:
# Implement stream URL extraction
response = self.client.get(f"https://example.anime.site/watch/{anime_id}/{episode}")
# Parse response and return list of Server objects
return [Server(...)]
```
## Player Plugins
Player plugins add support for new media players.
### Requirements
Your player class must inherit from `BasePlayer` and implement:
- `play(media_url: str, **kwargs) -> None`
### Example Player Plugin
**plugin.info.toml:**
```toml
[plugin]
name = "Custom Player"
version = "1.0.0"
description = "Adds support for my custom media player"
[components]
player = "custom_player:CustomPlayer"
```
**custom_player.py:**
```python
import subprocess
from viu_media.libs.player.base import BasePlayer
class CustomPlayer(BasePlayer):
def __init__(self, **config):
self.executable = config.get("executable", "my-player")
self.extra_args = config.get("extra_args", [])
def play(self, media_url: str, **kwargs) -> None:
cmd = [self.executable] + self.extra_args + [media_url]
subprocess.run(cmd)
```
## Selector Plugins
Selector plugins add support for new interactive selection tools.
### Requirements
Your selector class must inherit from `BaseSelector` and implement:
- `choose(choices: List[str], **kwargs) -> str`
- `confirm(message: str, **kwargs) -> bool`
- `ask(message: str, **kwargs) -> str`
## Command Plugins
Command plugins add new CLI commands to viu.
### Example Command Plugin
**plugin.info.toml:**
```toml
[plugin]
name = "My Command"
version = "1.0.0"
description = "Adds a custom command to viu"
[components]
command = "my_command:my_command"
```
**my_command.py:**
```python
import click
@click.command()
@click.argument("arg1")
def my_command(arg1: str):
"""My custom command description."""
click.echo(f"Hello from plugin command with arg: {arg1}")
```
## Plugin Configuration
Plugins can include a default configuration file (`config.toml`) in their repository root. When a plugin is installed, this default configuration is automatically copied to the user's `~/.config/viu/plugins.config.toml` file.
**Example `config.toml` in plugin repository:**
```toml
# Default configuration for My Plugin
[my-plugin-name]
timeout = 30
preferred_quality = "720p"
custom_option = "default_value"
```
**After installation, users can customize by editing `~/.config/viu/plugins.config.toml`:**
```toml
[my-plugin-name]
timeout = 60 # Customized value
preferred_quality = "1080p" # Customized value
custom_option = "my_value" # Customized value
```
Access this configuration in your plugin constructor via the `**config` parameter.
## Installation and Usage
### For Plugin Developers
1. Create your plugin repository following the structure above
2. Test your plugin locally
3. Publish your repository on GitHub/GitLab
4. Share the installation command with users
### For Users
Install a plugin:
```bash
viu plugin add --type provider myplugin github:user/viu-myplugin
```
Configure the plugin by editing `~/.config/viu/plugins.config.toml`:
```toml
[myplugin]
option1 = "value1"
option2 = "value2"
```
Use the plugin:
```bash
viu --provider myplugin search "anime name"
```
## Dependencies
If your plugin requires additional Python packages, include a `requirements.txt` file in your repository root. Users will need to install these manually:
```bash
pip install -r requirements.txt
```
## Best Practices
1. **Error Handling**: Implement proper error handling and logging
2. **Configuration**: Make your plugin configurable through the config system
3. **Documentation**: Include a README.md with usage instructions
4. **Testing**: Test your plugin thoroughly before publishing
5. **Versioning**: Use semantic versioning for your plugin releases
6. **Compatibility**: Specify minimum Python version requirements
## Plugin Management Commands
```bash
# Install a plugin
viu plugin add --type provider myplugin github:user/viu-myplugin
# List installed plugins
viu plugin list
viu plugin list --type provider
# Update a plugin
viu plugin update --type provider myplugin
# Remove a plugin
viu plugin remove --type provider myplugin
```
## Example Plugins
Check out these example plugin repositories:
- [Example Provider Plugin](https://github.com/example/viu-example-provider)
- [Example Player Plugin](https://github.com/example/viu-example-player)
## Support
For plugin development support:
- Open an issue in the main viu repository
- Join the Discord server: https://discord.gg/C4rhMA4mmK

View File

@@ -1,15 +1,3 @@
>[!IMPORTANT]
> looking for a new project name
>
>if you have any that is not already being used by someone on pypi please share on discord
>
>and let me warn yah am not good at naming things so help before disaster strikes again lol
>
>i dont want it to end up like viu where i added cli lol since viu was taken
>
>
<p align="center">
<h1 align="center">Viu</h1>
</p>

View File

@@ -0,0 +1,15 @@
[multi-file-provider]
# Base URL for the anime site
base_url = "https://multifile.example.site"
# Request timeout in seconds
timeout = 30
# Preferred video quality
preferred_quality = "720p"
# Maximum number of search results
max_results = 25
# Enable debug logging
debug = false

View File

@@ -0,0 +1,169 @@
"""
VLC player integration for Viu.
This module provides the VlcPlayer class, which implements the BasePlayer interface for the VLC media player.
"""
import logging
import shutil
import subprocess
from viu_media.core.config import VlcConfig
from viu_media.core.exceptions import ViuError
from viu_media.core.patterns import TORRENT_REGEX, YOUTUBE_REGEX
from viu_media.core.utils import detect
from viu_media.libs.player.base import BasePlayer
from viu_media.libs.player.params import PlayerParams
from viu_media.libs.player.types import PlayerResult
logger = logging.getLogger(__name__)
class VlcPlayer(BasePlayer):
"""
VLC player implementation for Viu.
Provides playback functionality using the VLC media player, supporting desktop, mobile, and torrent scenarios.
"""
def __init__(self, config: VlcConfig):
"""
Initialize the VlcPlayer with the given VLC configuration.
Args:
config: VlcConfig object containing VLC-specific settings.
"""
self.config = config
self.executable = shutil.which("vlc")
def play(self, params: PlayerParams) -> PlayerResult:
"""
Play the given media using VLC, handling desktop, mobile, and torrent scenarios.
Args:
params: PlayerParams object containing playback parameters.
Returns:
PlayerResult: Information about the playback session.
"""
if not self.executable:
raise ViuError("VLC executable not found in PATH.")
if TORRENT_REGEX.match(params.url) and detect.is_running_in_termux():
return self._play_on_mobile(params)
else:
return self._play_on_desktop(params)
def play_with_ipc(self, params: PlayerParams, socket_path: str) -> subprocess.Popen:
"""
Not implemented for VLC player.
"""
raise NotImplementedError("play_with_ipc is not implemented for VLC player.")
def _play_on_mobile(self, params: PlayerParams) -> PlayerResult:
"""
Play media on a mobile device using Android intents.
Args:
params: PlayerParams object containing playback parameters.
Returns:
PlayerResult: Information about the playback session.
"""
if YOUTUBE_REGEX.match(params.url):
args = [
"nohup",
"am",
"start",
"--user",
"0",
"-a",
"android.intent.action.VIEW",
"-d",
params.url,
"-n",
"com.google.android.youtube/.UrlActivity",
]
else:
args = [
"nohup",
"am",
"start",
"--user",
"0",
"-a",
"android.intent.action.VIEW",
"-d",
params.url,
"-n",
"org.videolan.vlc/org.videolan.vlc.gui.video.VideoPlayerActivity",
"-e",
"title",
params.title,
]
subprocess.run(args)
return PlayerResult(episode=params.episode)
def _play_on_desktop(self, params: PlayerParams) -> PlayerResult:
"""
Play media on a desktop environment using VLC.
Args:
params: PlayerParams object containing playback parameters.
Returns:
PlayerResult: Information about the playback session.
"""
if TORRENT_REGEX.search(params.url):
return self._stream_on_desktop_with_webtorrent_cli(params)
args = [self.executable, params.url]
if params.subtitles:
for sub in params.subtitles:
args.extend(["--sub-file", sub])
break
if params.title:
args.extend(["--video-title", params.title])
if self.config.args:
args.extend(self.config.args.split(","))
subprocess.run(args, encoding="utf-8")
return PlayerResult(episode=params.episode)
def _stream_on_desktop_with_webtorrent_cli(
self, params: PlayerParams
) -> PlayerResult:
"""
Stream torrent media using the webtorrent CLI and VLC.
Args:
params: PlayerParams object containing playback parameters.
Returns:
PlayerResult: Information about the playback session.
"""
WEBTORRENT_CLI = shutil.which("webtorrent")
if not WEBTORRENT_CLI:
raise ViuError("Please Install webtorrent cli inorder to stream torrents")
args = [WEBTORRENT_CLI, params.url, "--vlc"]
if self.config.args:
args.append("--player-args")
args.extend(self.config.args.split(","))
subprocess.run(args)
return PlayerResult(episode=params.episode)
if __name__ == "__main__":
from viu_media.core.constants import APP_ASCII_ART
print(APP_ASCII_ART)
url = input("Enter the url you would like to stream: ")
vlc = VlcPlayer(VlcConfig())
player_result = vlc.play(PlayerParams(url=url, title="", query="", episode=""))
print(player_result)

View File

@@ -0,0 +1,9 @@
[plugin]
name = "Multi-File Provider Plugin"
version = "1.0.0"
description = "A demo plugin with multiple Python files"
author = "Viu Developer"
requires_python = ">=3.11"
[components]
player = "player:VlcPlayer"

View File

@@ -0,0 +1,18 @@
# Default configuration for Example Provider Plugin
# This file is automatically copied to ~/.config/viu/plugins.config.toml during installation
[example-provider]
# Request timeout in seconds
timeout = 30
# Preferred video quality
preferred_quality = "720p"
# Maximum number of search results to return
max_results = 20
# Custom headers (optional)
# custom_header = "value"
# Enable debug logging for this plugin
# debug = false

View File

@@ -0,0 +1,37 @@
import re
ANIMEPAHE = "animepahe.ru"
ANIMEPAHE_BASE = f"https://{ANIMEPAHE}"
ANIMEPAHE_ENDPOINT = f"{ANIMEPAHE_BASE}/api"
SERVERS_AVAILABLE = ["kwik"]
REQUEST_HEADERS = {
"Cookie": "__ddgid_=VvX0ebHrH2DsFZo4; __ddgmark_=3savRpSVFhvZcn5x; __ddg2_=buBJ3c4pNBYKFZNp; __ddg1_=rbVADKr9URtt55zoIGFa; SERVERID=janna; XSRF-TOKEN=eyJpdiI6IjV5bFNtd0phUHgvWGJxc25wL0VJSUE9PSIsInZhbHVlIjoicEJTZktlR2hxR2JZTWhnL0JzazlvZU5TQTR2bjBWZ2dDb0RwUXVUUWNSclhQWUhLRStYSmJmWmUxWkpiYkFRYU12RjFWejlSWHorME1wZG5qQ1U0TnFlNnBFR2laQjN1MjdyNjc5TjVPdXdJb2o5VkU1bEduRW9pRHNDTHh6Sy8iLCJtYWMiOiI0OTc0ZmNjY2UwMGJkOWY2MWNkM2NlMjk2ZGMyZGJmMWE0NTdjZTdkNGI2Y2IwNTIzZmFiZWU5ZTE2OTk0YmU4IiwidGFnIjoiIn0%3D; laravel_session=eyJpdiI6ImxvdlpqREFnTjdaeFJubUlXQWlJVWc9PSIsInZhbHVlIjoiQnE4R3VHdjZ4M1NDdEVWM1ZqMUxtNnVERnJCcmtCUHZKNzRPR2RFbzNFcStTL29xdnVTbWhsNVRBUXEybVZWNU1UYVlTazFqYlN5UjJva1k4czNGaXBTbkJJK01oTUd3VHRYVHBoc3dGUWxHYnFlS2NJVVNFbTFqMVBWdFpuVUgiLCJtYWMiOiI1NDdjZTVkYmNhNjUwZTMxZmRlZmVmMmRlMGNiYjAwYjlmYjFjY2U0MDc1YTQzZThiMTIxMjJlYTg1NTA4YjBmIiwidGFnIjoiIn0%3D; latest=5592",
"Host": ANIMEPAHE,
"Accept": "application, text/javascript, */*; q=0.01",
"Accept-Encoding": "Utf-8",
"Referer": ANIMEPAHE_BASE,
"DNT": "1",
"Connection": "keep-alive",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-Mode": "cors",
"TE": "trailers",
}
SERVER_HEADERS = {
"Host": "kwik.si",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "Utf-8",
"DNT": "1",
"Connection": "keep-alive",
"Referer": "https://animepahe.ru/",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "iframe",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "cross-site",
"Priority": "u=4",
"TE": "trailers",
}
JUICY_STREAM_REGEX = re.compile(r"source='(.*)';")
KWIK_RE = re.compile(r"Player\|(.+?)'")

View File

@@ -0,0 +1,77 @@
import re
def animepahe_key_creator(c: int, a: int):
from ...scraping.utils import encode_base_n
if c < a:
val_a = ""
else:
val_a = animepahe_key_creator(int(c / a), a)
c = c % a
if c > 35:
val_b = chr(c + 29)
else:
val_b = encode_base_n(c, 36)
return val_a + val_b
def animepahe_embed_decoder(
encoded_js_p: str,
base_a: int,
no_of_keys_c: int,
values_to_replace_with_k: list,
):
decode_mapper_d: dict = {}
for i in range(no_of_keys_c):
key = animepahe_key_creator(i, base_a)
val = values_to_replace_with_k[i] or key
decode_mapper_d[key] = val
return re.sub(
r"\b\w+\b", lambda match: decode_mapper_d[match.group(0)], encoded_js_p
)
PARAMETERS_REGEX = re.compile(r"eval\(function\(p,a,c,k,e,d\)\{.*\}\((.*?)\)\)$")
ENCODE_JS_REGEX = re.compile(r"'(.*?);',(\d+),(\d+),'(.*)'\.split")
def process_animepahe_embed_page(embed_page: str):
from ...scraping.html_parser import get_element_text_and_html_by_tag
encoded_js_string = ""
embed_page_content = embed_page
for _ in range(8):
text, html = get_element_text_and_html_by_tag("script", embed_page_content)
if not text and html:
embed_page_content = re.sub(html, "", embed_page_content)
continue
if text:
encoded_js_string = text.strip()
break
if not encoded_js_string:
return
obsfucated_js_parameter_match = PARAMETERS_REGEX.search(encoded_js_string)
if not obsfucated_js_parameter_match:
return
parameter_string = obsfucated_js_parameter_match.group(1)
encoded_js_parameter_string = ENCODE_JS_REGEX.search(parameter_string)
if not encoded_js_parameter_string:
return
p: str = encoded_js_parameter_string.group(1)
a: int = int(encoded_js_parameter_string.group(2))
c: int = int(encoded_js_parameter_string.group(3))
k: list = encoded_js_parameter_string.group(4).split("|")
return animepahe_embed_decoder(p, a, c, k).replace("\\", "")
if __name__ == "__main__":
# Testing time
filepath = input("Enter file name: ")
if filepath:
with open(filepath) as file:
data = file.read()
else:
data = """<script>eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('f $7={H:a(2){4 B(9.7.h(y z("(?:(?:^|.*;)\\\\s*"+d(2).h(/[\\-\\.\\+\\*]/g,"\\\\$&")+"\\\\s*\\\\=\\\\s*([^;]*).*$)|^.*$"),"$1"))||G},E:a(2,q,3,6,5,t){k(!2||/^(?:8|r\\-v|o|m|p)$/i.D(2)){4 w}f b="";k(3){F(3.J){j K:b=3===P?"; 8=O, I N Q M:u:u A":"; r-v="+3;n;j L:b="; 8="+3;n;j S:b="; 8="+3.Z();n}}9.7=d(2)+"="+d(q)+b+(5?"; m="+5:"")+(6?"; o="+6:"")+(t?"; p":"");4 x},Y:a(2,6,5){k(!2||!11.C(2)){4 w}9.7=d(2)+"=; 8=12, R 10 W l:l:l A"+(5?"; m="+5:"")+(6?"; o="+6:"");4 x},C:a(2){4(y z("(?:^|;\\\\s*)"+d(2).h(/[\\-\\.\\+\\*]/g,"\\\\$&")+"\\\\s*\\\\=")).D(9.7)},X:a(){f c=9.7.h(/((?:^|\\s*;)[^\\=]+)(?=;|$)|^\\s*|\\s*(?:\\=[^;]*)?(?:\\1|$)/g,"").T(/\\s*(?:\\=[^;]*)?;\\s*/);U(f e=0;e<c.V;e++){c[e]=B(c[e])}4 c}};',62,65,'||sKey|vEnd|return|sDomain|sPath|cookie|expires|document|function|sExpires|aKeys|encodeURIComponent|nIdx|var||replace||case|if|00|domain|break|path|secure|sValue|max||bSecure|59|age|false|true|new|RegExp|GMT|decodeURIComponent|hasItem|test|setItem|switch|null|getItem|31|constructor|Number|String|23|Dec|Fri|Infinity|9999|01|Date|split|for|length|1970|keys|removeItem|toUTCString|Jan|this|Thu'.split('|'),0,{}));eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('h o=\'1D://1C-E.1B.1A.1z/1y/E/1x/1w/1v.1u\';h d=s.r(\'d\');h 0=B 1t(d,{\'1s\':{\'1r\':i},\'1q\':\'16:9\',\'D\':1,\'1p\':5,\'1o\':{\'1n\':\'1m\'},1l:[\'7-1k\',\'7\',\'1j\',\'1i-1h\',\'1g\',\'1f-1e\',\'1d\',\'D\',\'1c\',\'1b\',\'1a\',\'19\',\'C\',\'18\'],\'C\':{\'17\':i}});8(!A.15()){d.14=o}x{j z={13:12,11:10,Z:Y,X:i,W:i};h c=B A(z);c.V(o);c.U(d);g.c=c}0.3("T",6=>{g.S.R.Q("P")});0.O=1;k v(b,n,m){8(b.y){b.y(n,m,N)}x 8(b.w){b.w(\'3\'+n,m)}}j 4=k(l){g.M.L(l,\'*\')};v(g,\'l\',k(e){j a=e.a;8(a===\'7\')0.7();8(a===\'f\')0.f();8(a===\'u\')0.u()});0.3(\'t\',6=>{4(\'t\')});0.3(\'7\',6=>{4(\'7\')});0.3(\'f\',6=>{4(\'f\')});0.3(\'K\',6=>{4(0.q);s.r(\'.J-I\').H=G(0.q.F(2))});0.3(\'p\',6=>{4(\'p\')});',62,102,'player|||on|sendMessage||event|play|if||data|element|hls|video||pause|window|const|true|var|function|message|eventHandler|eventName|source|ended|currentTime|querySelector|document|ready|stop|bindEvent|attachEvent|else|addEventListener|config|Hls|new|fullscreen|volume|01|toFixed|String|innerHTML|timestamp|ss|timeupdate|postMessage|parent|false|speed|landscape|lock|orientation|screen|enterfullscreen|attachMedia|loadSource|lowLatencyMode|enableWorker|Infinity|backBufferLength|600|maxMaxBufferLength|180|maxBufferLength|src|isSupported||iosNative|capture|airplay|pip|settings|captions|mute|time|current|progress|forward|fast|rewind|large|controls|kwik|key|storage|seekTime|ratio|global|keyboard|Plyr|m3u8|uwu|b92a392054c041a3f9c6eecabeb0e127183f44e547828447b10bca8d77523e6f|03|stream|org|nextcdn|files|eu|https'.split('|'),0,{}))</script>"""
print(process_animepahe_embed_page(data))

View File

@@ -0,0 +1,100 @@
from typing import Any
from viu_media.libs.provider.anime.types import (
Anime,
AnimeEpisodeInfo,
AnimeEpisodes,
EpisodeStream,
MediaTranslationType,
PageInfo,
SearchResult,
SearchResults,
Server,
)
from .types import (
AnimePaheAnimePage,
AnimePaheSearchPage,
)
translation_type_map = {
"sub": MediaTranslationType.SUB,
"dub": MediaTranslationType.DUB,
"raw": MediaTranslationType.RAW,
}
def map_to_search_results(data: AnimePaheSearchPage) -> SearchResults:
results = []
for result in data["data"]:
results.append(
SearchResult(
id=result["session"],
title=result["title"],
episodes=AnimeEpisodes(
sub=list(map(str, range(1, result["episodes"] + 1))),
dub=list(map(str, range(1, result["episodes"] + 1))),
raw=list(map(str, range(1, result["episodes"] + 1))),
),
media_type=result["type"],
score=result["score"],
status=result["status"],
season=result["season"],
poster=result["poster"],
year=str(result["year"]),
)
)
return SearchResults(
page_info=PageInfo(
total=data["total"],
per_page=data["per_page"],
current_page=data["current_page"],
),
results=results,
)
def map_to_anime_result(
search_result: SearchResult, anime: AnimePaheAnimePage
) -> Anime:
episodes_info = []
episodes = []
anime["data"] = sorted(anime["data"], key=lambda k: float(k["episode"]))
for ep_info in anime["data"]:
episodes.append(str(ep_info["episode"]))
episodes_info.append(
AnimeEpisodeInfo(
id=str(ep_info["id"]),
session_id=ep_info["session"],
episode=str(ep_info["episode"]),
title=ep_info["title"],
poster=ep_info["snapshot"],
duration=str(ep_info["duration"]),
)
)
return Anime(
id=search_result.id,
title=search_result.title,
episodes=AnimeEpisodes(
sub=episodes,
dub=episodes,
),
year=str(search_result.year),
poster=search_result.poster,
episodes_info=episodes_info,
)
def map_to_server(
episode: AnimeEpisodeInfo, translation_type: Any, quality: Any, stream_link: Any
) -> Server:
links = [
EpisodeStream(
link=stream_link,
quality=quality,
translation_type=translation_type_map[translation_type],
)
]
return Server(name="kwik", links=links, episode_title=episode.title)

View File

@@ -0,0 +1,10 @@
[plugin]
name = "Example Provider Plugin"
version = "1.0.0"
description = "A demo provider plugin for testing the viu plugin system"
author = "Viu Developer"
homepage = "https://github.com/example/viu-example-plugin"
requires_python = ">=3.11"
[components]
provider = "example_provider:ExampleProvider"

View File

@@ -0,0 +1,207 @@
import logging
from functools import lru_cache
from typing import Iterator, Optional
from viu_media.libs.provider.anime.base import BaseAnimeProvider
from viu_media.libs.provider.anime.params import (
AnimeParams,
EpisodeStreamsParams,
SearchParams,
)
from viu_media.libs.provider.anime.types import (
Anime,
AnimeEpisodeInfo,
SearchResult,
SearchResults,
Server,
)
from viu_media.libs.provider.anime.utils.debug import debug_provider
from .constants import (
ANIMEPAHE_BASE,
ANIMEPAHE_ENDPOINT,
JUICY_STREAM_REGEX,
REQUEST_HEADERS,
SERVER_HEADERS,
)
from .extractor import process_animepahe_embed_page
from .mappers import map_to_anime_result, map_to_search_results, map_to_server
from .types import AnimePaheAnimePage, AnimePaheSearchPage
logger = logging.getLogger(__name__)
class AnimePahe(BaseAnimeProvider):
HEADERS = REQUEST_HEADERS
@debug_provider
def search(self, params: SearchParams) -> SearchResults | None:
return self._search(params)
@lru_cache()
def _search(self, params: SearchParams) -> SearchResults | None:
url_params = {"m": "search", "q": params.query}
response = self.client.get(ANIMEPAHE_ENDPOINT, params=url_params)
response.raise_for_status()
data: AnimePaheSearchPage = response.json()
if not data.get("data"):
return
return map_to_search_results(data)
@debug_provider
def get(self, params: AnimeParams) -> Anime | None:
return self._get_anime(params)
@lru_cache()
def _get_anime(self, params: AnimeParams) -> Anime | None:
page = 1
standardized_episode_number = 0
search_result = self._get_search_result(params)
if not search_result:
logger.error(f"No search result found for ID {params.id}")
return None
anime: Optional[AnimePaheAnimePage] = None
has_next_page = True
while has_next_page:
logger.debug(f"Loading page: {page}")
_anime_page = self._anime_page_loader(
m="release",
id=params.id,
sort="episode_asc",
page=page,
)
has_next_page = True if _anime_page["next_page_url"] else False
page += 1
if not anime:
anime = _anime_page
else:
anime["data"].extend(_anime_page["data"])
if anime:
for episode in anime.get("data", []):
if episode["episode"] % 1 == 0:
standardized_episode_number += 1
episode.update({"episode": standardized_episode_number})
else:
standardized_episode_number += episode["episode"] % 1
episode.update({"episode": standardized_episode_number})
standardized_episode_number = int(standardized_episode_number)
return map_to_anime_result(search_result, anime)
@lru_cache()
def _get_search_result(self, params: AnimeParams) -> Optional[SearchResult]:
search_results = self._search(SearchParams(query=params.query))
if not search_results or not search_results.results:
logger.error(f"No search results found for ID {params.id}")
return None
for search_result in search_results.results:
if search_result.id == params.id:
return search_result
@lru_cache()
def _anime_page_loader(self, m, id, sort, page) -> AnimePaheAnimePage:
url_params = {
"m": m,
"id": id,
"sort": sort,
"page": page,
}
response = self.client.get(ANIMEPAHE_ENDPOINT, params=url_params)
response.raise_for_status()
return response.json()
@debug_provider
def episode_streams(self, params: EpisodeStreamsParams) -> Iterator[Server] | None:
from viu_media.libs.provider.scraping.html_parser import (
extract_attributes,
get_element_by_id,
get_elements_html_by_class,
)
episode = self._get_episode_info(params)
if not episode:
logger.error(
f"Episode {params.episode} doesn't exist for anime {params.anime_id}"
)
return
url = f"{ANIMEPAHE_BASE}/play/{params.anime_id}/{episode.session_id}"
response = self.client.get(url, follow_redirects=True)
response.raise_for_status()
c = get_element_by_id("resolutionMenu", response.text)
if not c:
logger.error("Resolution menu not found in the response")
return
resolutionMenuItems = get_elements_html_by_class("dropdown-item", c)
res_dicts = [extract_attributes(item) for item in resolutionMenuItems]
quality = None
translation_type = None
stream_link = None
# TODO: better document the scraping process
for res_dict in res_dicts:
# the actual attributes are data attributes in the original html 'prefixed with data-'
embed_url = res_dict["src"]
data_audio = "dub" if res_dict["audio"] == "eng" else "sub"
if data_audio != params.translation_type:
continue
if not embed_url:
logger.warning("embed url not found please report to the developers")
continue
embed_response = self.client.get(
embed_url,
headers={
"User-Agent": self.client.headers["User-Agent"],
**SERVER_HEADERS,
},
)
embed_response.raise_for_status()
embed_page = embed_response.text
decoded_js = process_animepahe_embed_page(embed_page)
if not decoded_js:
logger.error("failed to decode embed page")
continue
juicy_stream = JUICY_STREAM_REGEX.search(decoded_js)
if not juicy_stream:
logger.error("failed to find juicy stream")
continue
juicy_stream = juicy_stream.group(1)
quality = res_dict["resolution"]
translation_type = data_audio
stream_link = juicy_stream
if translation_type and quality and stream_link:
yield map_to_server(episode, translation_type, quality, stream_link)
@lru_cache()
def _get_episode_info(
self, params: EpisodeStreamsParams
) -> Optional[AnimeEpisodeInfo]:
anime_info = self._get_anime(
AnimeParams(id=params.anime_id, query=params.query)
)
if not anime_info:
logger.error(f"No anime info for {params.anime_id}")
return
if not anime_info.episodes_info:
logger.error(f"No episodes info for {params.anime_id}")
return
for episode in anime_info.episodes_info:
if episode.episode == params.episode:
return episode
if __name__ == "__main__":
from viu_media.libs.provider.anime.utils.debug import test_anime_provider
test_anime_provider(AnimePahe)

View File

@@ -0,0 +1,108 @@
from enum import Enum
from typing import Literal, TypedDict
class Server(Enum):
KWIK = "Kwik"
class AnimePaheSearchResult(TypedDict):
id: str
title: str
type: str
episodes: int
status: str
season: str
year: int
score: int
poster: str
session: str
class AnimePaheSearchPage(TypedDict):
total: int
per_page: int
current_page: int
last_page: int
_from: int
to: int
data: list[AnimePaheSearchResult]
class Episode(TypedDict):
id: str
anime_id: int
episode: float
episode2: int
edition: str
title: str
snapshot: str # episode image
disc: str
audio: Literal["eng", "jpn"]
duration: str # time 00:00:00
session: str
filler: int
created_at: str
class AnimePaheAnimePage(TypedDict):
total: int
per_page: int
current_page: int
last_page: int
next_page_url: str | None
prev_page_url: str | None
_from: int
to: int
data: list[Episode]
class AnimePaheEpisodeInfo(TypedDict):
title: str
episode: float
id: str
translation_type: Literal["eng", "jpn"]
duration: str
poster: str
class AvailableEpisodesDetail(TypedDict):
sub: list[str]
dub: list[str]
raw: list[str]
class AnimePaheAnime(TypedDict):
id: str
title: str
year: int
season: str
poster: str
score: int
availableEpisodesDetail: AvailableEpisodesDetail
episodesInfo: list[AnimePaheEpisodeInfo]
class PageInfo(TypedDict):
total: int
perPage: int
currentPage: int
class AnimePaheSearchResults(TypedDict):
pageInfo: PageInfo
results: list[AnimePaheSearchResult]
class AnimePaheStreamLink(TypedDict):
quality: str
translation_type: Literal["sub", "dub"]
link: str
class AnimePaheServer(TypedDict):
server: Literal["kwik"]
links: list[AnimePaheStreamLink]
episode_title: str
subtitles: list
headers: dict

View File

@@ -4,13 +4,14 @@ version = "3.2.7"
description = "A browser anime site experience from the terminal"
license = "UNLICENSE"
readme = "README.md"
requires-python = ">=3.10"
requires-python = ">=3.11"
dependencies = [
"click>=8.1.7",
"httpx>=0.28.1",
"inquirerpy>=0.3.4",
"pydantic>=2.11.7",
"rich>=13.9.2",
"tomli-w>=1.0.0",
]
[project.scripts]

View File

@@ -1,5 +1,5 @@
{
"venvPath": ".",
"venv": ".venv",
"pythonVersion": "3.10"
"pythonVersion": "3.11"
}

View File

@@ -1,7 +1,7 @@
[tox]
requires =
tox>=4
env_list = lint, pyright, py{310,311}
env_list = lint, pyright, py{311,312}
[testenv]
description = run unit tests

242
uv.lock generated
View File

@@ -1,6 +1,6 @@
version = 1
revision = 2
requires-python = ">=3.10"
requires-python = ">=3.11"
[[package]]
name = "altgraph"
@@ -25,7 +25,6 @@ name = "anyio"
version = "4.10.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "idna" },
{ name = "sniffio" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
@@ -89,18 +88,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" },
]
[[package]]
name = "exceptiongroup"
version = "1.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" },
]
[[package]]
name = "filelock"
version = "3.19.1"
@@ -192,15 +179,6 @@ name = "libtorrent"
version = "2.0.11"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ba/a5/29efe1dba2d0e6eb18ceab7875be8ae186da5dc5c08aeb8cfe9e7713d935/libtorrent-2.0.11-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:32abb3a0488d489f5d74a3c5fb164f0e445a9890af8d8bed5b617f5b145b3cf5", size = 5547129, upload-time = "2025-01-29T11:33:26.897Z" },
{ url = "https://files.pythonhosted.org/packages/bd/82/a0741411bb1ed718a9fd4da7638aceb2d1a8cacadf11b4019239334ae511/libtorrent-2.0.11-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:684d9a1f06d4437735ea7aa1efb744cf9c8be28bf54f033e854516b8640f3eb4", size = 5648784, upload-time = "2025-01-29T11:33:31.508Z" },
{ url = "https://files.pythonhosted.org/packages/8b/62/24927c44925fe530236f1f668a21227321af0f1a19f932324500bb879acf/libtorrent-2.0.11-cp310-cp310-macosx_15_0_arm64.whl", hash = "sha256:6639904a349532bbd785344f83849cd03bfc671f272a6701fc994ec77eaa9230", size = 5647654, upload-time = "2025-01-29T11:33:33.991Z" },
{ url = "https://files.pythonhosted.org/packages/13/31/4fa8ce41a45759e231e64af3c01fbaa390abc843aeaa7b2a05fd41df543f/libtorrent-2.0.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:932e519299442e8dcd1b85cec84170b967e05ebc048e6b91b3212de8276b4f51", size = 8259893, upload-time = "2025-01-29T11:33:36.153Z" },
{ url = "https://files.pythonhosted.org/packages/f8/f3/dad03aa0cbfbbe57dcba7ac8ab4bdc5110d1559b1129ff1bf2dd9a715ec9/libtorrent-2.0.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68d739dd51e5c2a0deb6822fdf4434113016fd942a70569813bfee3f1184a15b", size = 8519612, upload-time = "2025-01-29T11:33:39.518Z" },
{ url = "https://files.pythonhosted.org/packages/ad/06/a8adc75b71c179fd81d4b58347c2ce58a296952e358cb9eb0aeb9d2a34ed/libtorrent-2.0.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1a09b0287ea545b3bc31552792db31d3c8999b179b0dbae6090b7986913d97ed", size = 12086901, upload-time = "2025-01-29T11:33:43.519Z" },
{ url = "https://files.pythonhosted.org/packages/ac/1b/8e7ceb19bedc19a6474b52f18ad17643bb84307d3d97e47f10ca2b078df0/libtorrent-2.0.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5f2354c596bd85b212c81daa84ea017b87fbe35aafbf167eb3b73d4be10a8e52", size = 12062955, upload-time = "2025-01-29T11:33:48.01Z" },
{ url = "https://files.pythonhosted.org/packages/58/d1/6074ddb437719d92059e5294266a85c0a996e17bbc7ac0cec8993f204e91/libtorrent-2.0.11-cp310-cp310-win32.whl", hash = "sha256:f29bbf659272c46c14b333c945e38282907c6c1784fd73af370fa475361bff84", size = 1772634, upload-time = "2025-01-29T11:33:51.782Z" },
{ url = "https://files.pythonhosted.org/packages/3a/d1/bf1317d4c604078a23535701ca666cc7df35b2587756965a8ccbf10f63f7/libtorrent-2.0.11-cp310-cp310-win_amd64.whl", hash = "sha256:cf681e540d9c71d580262b64573be67ffe98e85a2fa22cf68cdbc9766878a7ef", size = 1969854, upload-time = "2025-01-29T11:33:54.65Z" },
{ url = "https://files.pythonhosted.org/packages/2a/32/11c1343e3f64859acba2448d1fc857af94b336f7e857e607abf24c191eb3/libtorrent-2.0.11-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:5041f69119a4c0dc2c586a4cfa72b75bd3591561369da97abaa6fab9ed8bb0db", size = 5547062, upload-time = "2025-01-29T11:33:57.4Z" },
{ url = "https://files.pythonhosted.org/packages/a0/e4/e472f2d0e8623399dbfcf0048f68f90760520bb08665c0cadc0a16cda346/libtorrent-2.0.11-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4463ffa568cbfcc348098f15c7cfc8afccdbae2a63d929de9c45a929a0865b56", size = 5648816, upload-time = "2025-01-29T11:34:00.5Z" },
{ url = "https://files.pythonhosted.org/packages/2b/96/15a4c4ebc9a86b7bb4d348c1a7064710adfb5d379419e1879f0a6df3f314/libtorrent-2.0.11-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:93627846dd1567cbd961b23e4314d1661a1c64797338518837a35c5bb702a61c", size = 5647624, upload-time = "2025-01-29T11:34:03.522Z" },
@@ -236,20 +214,6 @@ version = "6.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c5/ed/60eb6fa2923602fba988d9ca7c5cdbd7cf25faa795162ed538b527a35411/lxml-6.0.0.tar.gz", hash = "sha256:032e65120339d44cdc3efc326c9f660f5f7205f3a535c1fdbf898b29ea01fb72", size = 4096938, upload-time = "2025-06-26T16:28:19.373Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4b/e9/9c3ca02fbbb7585116c2e274b354a2d92b5c70561687dd733ec7b2018490/lxml-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:35bc626eec405f745199200ccb5c6b36f202675d204aa29bb52e27ba2b71dea8", size = 8399057, upload-time = "2025-06-26T16:25:02.169Z" },
{ url = "https://files.pythonhosted.org/packages/86/25/10a6e9001191854bf283515020f3633b1b1f96fd1b39aa30bf8fff7aa666/lxml-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:246b40f8a4aec341cbbf52617cad8ab7c888d944bfe12a6abd2b1f6cfb6f6082", size = 4569676, upload-time = "2025-06-26T16:25:05.431Z" },
{ url = "https://files.pythonhosted.org/packages/f5/a5/378033415ff61d9175c81de23e7ad20a3ffb614df4ffc2ffc86bc6746ffd/lxml-6.0.0-cp310-cp310-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:2793a627e95d119e9f1e19720730472f5543a6d84c50ea33313ce328d870f2dd", size = 5291361, upload-time = "2025-06-26T16:25:07.901Z" },
{ url = "https://files.pythonhosted.org/packages/5a/a6/19c87c4f3b9362b08dc5452a3c3bce528130ac9105fc8fff97ce895ce62e/lxml-6.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:46b9ed911f36bfeb6338e0b482e7fe7c27d362c52fde29f221fddbc9ee2227e7", size = 5008290, upload-time = "2025-06-28T18:47:13.196Z" },
{ url = "https://files.pythonhosted.org/packages/09/d1/e9b7ad4b4164d359c4d87ed8c49cb69b443225cb495777e75be0478da5d5/lxml-6.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b4790b558bee331a933e08883c423f65bbcd07e278f91b2272489e31ab1e2b4", size = 5163192, upload-time = "2025-06-28T18:47:17.279Z" },
{ url = "https://files.pythonhosted.org/packages/56/d6/b3eba234dc1584744b0b374a7f6c26ceee5dc2147369a7e7526e25a72332/lxml-6.0.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2030956cf4886b10be9a0285c6802e078ec2391e1dd7ff3eb509c2c95a69b76", size = 5076973, upload-time = "2025-06-26T16:25:10.936Z" },
{ url = "https://files.pythonhosted.org/packages/8e/47/897142dd9385dcc1925acec0c4afe14cc16d310ce02c41fcd9010ac5d15d/lxml-6.0.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d23854ecf381ab1facc8f353dcd9adeddef3652268ee75297c1164c987c11dc", size = 5297795, upload-time = "2025-06-26T16:25:14.282Z" },
{ url = "https://files.pythonhosted.org/packages/fb/db/551ad84515c6f415cea70193a0ff11d70210174dc0563219f4ce711655c6/lxml-6.0.0-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:43fe5af2d590bf4691531b1d9a2495d7aab2090547eaacd224a3afec95706d76", size = 4776547, upload-time = "2025-06-26T16:25:17.123Z" },
{ url = "https://files.pythonhosted.org/packages/e0/14/c4a77ab4f89aaf35037a03c472f1ccc54147191888626079bd05babd6808/lxml-6.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74e748012f8c19b47f7d6321ac929a9a94ee92ef12bc4298c47e8b7219b26541", size = 5124904, upload-time = "2025-06-26T16:25:19.485Z" },
{ url = "https://files.pythonhosted.org/packages/70/b4/12ae6a51b8da106adec6a2e9c60f532350a24ce954622367f39269e509b1/lxml-6.0.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:43cfbb7db02b30ad3926e8fceaef260ba2fb7df787e38fa2df890c1ca7966c3b", size = 4805804, upload-time = "2025-06-26T16:25:21.949Z" },
{ url = "https://files.pythonhosted.org/packages/a9/b6/2e82d34d49f6219cdcb6e3e03837ca5fb8b7f86c2f35106fb8610ac7f5b8/lxml-6.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34190a1ec4f1e84af256495436b2d196529c3f2094f0af80202947567fdbf2e7", size = 5323477, upload-time = "2025-06-26T16:25:24.475Z" },
{ url = "https://files.pythonhosted.org/packages/a1/e6/b83ddc903b05cd08a5723fefd528eee84b0edd07bdf87f6c53a1fda841fd/lxml-6.0.0-cp310-cp310-win32.whl", hash = "sha256:5967fe415b1920a3877a4195e9a2b779249630ee49ece22021c690320ff07452", size = 3613840, upload-time = "2025-06-26T16:25:27.345Z" },
{ url = "https://files.pythonhosted.org/packages/40/af/874fb368dd0c663c030acb92612341005e52e281a102b72a4c96f42942e1/lxml-6.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:f3389924581d9a770c6caa4df4e74b606180869043b9073e2cec324bad6e306e", size = 3993584, upload-time = "2025-06-26T16:25:29.391Z" },
{ url = "https://files.pythonhosted.org/packages/4a/f4/d296bc22c17d5607653008f6dd7b46afdfda12efd31021705b507df652bb/lxml-6.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:522fe7abb41309e9543b0d9b8b434f2b630c5fdaf6482bee642b34c8c70079c8", size = 3681400, upload-time = "2025-06-26T16:25:31.421Z" },
{ url = "https://files.pythonhosted.org/packages/7c/23/828d4cc7da96c611ec0ce6147bbcea2fdbde023dc995a165afa512399bbf/lxml-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ee56288d0df919e4aac43b539dd0e34bb55d6a12a6562038e8d6f3ed07f9e36", size = 8438217, upload-time = "2025-06-26T16:25:34.349Z" },
{ url = "https://files.pythonhosted.org/packages/f1/33/5ac521212c5bcb097d573145d54b2b4a3c9766cda88af5a0e91f66037c6e/lxml-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8dd6dd0e9c1992613ccda2bcb74fc9d49159dbe0f0ca4753f37527749885c25", size = 4590317, upload-time = "2025-06-26T16:25:38.103Z" },
{ url = "https://files.pythonhosted.org/packages/2b/2e/45b7ca8bee304c07f54933c37afe7dd4d39ff61ba2757f519dcc71bc5d44/lxml-6.0.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:d7ae472f74afcc47320238b5dbfd363aba111a525943c8a34a1b657c6be934c3", size = 5221628, upload-time = "2025-06-26T16:25:40.878Z" },
@@ -296,12 +260,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5f/01/d48cc141bc47bc1644d20fe97bbd5e8afb30415ec94f146f2f76d0d9d098/lxml-6.0.0-cp313-cp313-win32.whl", hash = "sha256:5fcd7d3b1d8ecb91445bd71b9c88bdbeae528fefee4f379895becfc72298d181", size = 3612896, upload-time = "2025-06-26T16:27:04.251Z" },
{ url = "https://files.pythonhosted.org/packages/f4/87/6456b9541d186ee7d4cb53bf1b9a0d7f3b1068532676940fdd594ac90865/lxml-6.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:2f34687222b78fff795feeb799a7d44eca2477c3d9d3a46ce17d51a4f383e32e", size = 4013132, upload-time = "2025-06-26T16:27:06.415Z" },
{ url = "https://files.pythonhosted.org/packages/b7/42/85b3aa8f06ca0d24962f8100f001828e1f1f1a38c954c16e71154ed7d53a/lxml-6.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:21db1ec5525780fd07251636eb5f7acb84003e9382c72c18c542a87c416ade03", size = 3672642, upload-time = "2025-06-26T16:27:09.888Z" },
{ url = "https://files.pythonhosted.org/packages/66/e1/2c22a3cff9e16e1d717014a1e6ec2bf671bf56ea8716bb64466fcf820247/lxml-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:dbdd7679a6f4f08152818043dbb39491d1af3332128b3752c3ec5cebc0011a72", size = 3898804, upload-time = "2025-06-26T16:27:59.751Z" },
{ url = "https://files.pythonhosted.org/packages/2b/3a/d68cbcb4393a2a0a867528741fafb7ce92dac5c9f4a1680df98e5e53e8f5/lxml-6.0.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:40442e2a4456e9910875ac12951476d36c0870dcb38a68719f8c4686609897c4", size = 4216406, upload-time = "2025-06-28T18:47:45.518Z" },
{ url = "https://files.pythonhosted.org/packages/15/8f/d9bfb13dff715ee3b2a1ec2f4a021347ea3caf9aba93dea0cfe54c01969b/lxml-6.0.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db0efd6bae1c4730b9c863fc4f5f3c0fa3e8f05cae2c44ae141cb9dfc7d091dc", size = 4326455, upload-time = "2025-06-28T18:47:48.411Z" },
{ url = "https://files.pythonhosted.org/packages/01/8b/fde194529ee8a27e6f5966d7eef05fa16f0567e4a8e8abc3b855ef6b3400/lxml-6.0.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ab542c91f5a47aaa58abdd8ea84b498e8e49fe4b883d67800017757a3eb78e8", size = 4268788, upload-time = "2025-06-26T16:28:02.776Z" },
{ url = "https://files.pythonhosted.org/packages/99/a8/3b8e2581b4f8370fc9e8dc343af4abdfadd9b9229970fc71e67bd31c7df1/lxml-6.0.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:013090383863b72c62a702d07678b658fa2567aa58d373d963cca245b017e065", size = 4411394, upload-time = "2025-06-26T16:28:05.179Z" },
{ url = "https://files.pythonhosted.org/packages/e7/a5/899a4719e02ff4383f3f96e5d1878f882f734377f10dfb69e73b5f223e44/lxml-6.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c86df1c9af35d903d2b52d22ea3e66db8058d21dc0f59842ca5deb0595921141", size = 3517946, upload-time = "2025-06-26T16:28:07.665Z" },
]
[[package]]
@@ -465,11 +423,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/8d/67/09ee8500dd22614af5fbaa51a4aee6e342b5fa8aecf0a6cb9cbf52fa6d45/pycryptodomex-3.23.0-cp37-abi3-win32.whl", hash = "sha256:189afbc87f0b9f158386bf051f720e20fa6145975f1e76369303d0f31d1a8d7c", size = 1771969, upload-time = "2025-05-17T17:23:07.115Z" },
{ url = "https://files.pythonhosted.org/packages/69/96/11f36f71a865dd6df03716d33bd07a67e9d20f6b8d39820470b766af323c/pycryptodomex-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:52e5ca58c3a0b0bd5e100a9fbc8015059b05cffc6c66ce9d98b4b45e023443b9", size = 1803124, upload-time = "2025-05-17T17:23:09.267Z" },
{ url = "https://files.pythonhosted.org/packages/f9/93/45c1cdcbeb182ccd2e144c693eaa097763b08b38cded279f0053ed53c553/pycryptodomex-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:02d87b80778c171445d67e23d1caef279bf4b25c3597050ccd2e13970b57fd51", size = 1707161, upload-time = "2025-05-17T17:23:11.414Z" },
{ url = "https://files.pythonhosted.org/packages/f3/b8/3e76d948c3c4ac71335bbe75dac53e154b40b0f8f1f022dfa295257a0c96/pycryptodomex-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ebfff755c360d674306e5891c564a274a47953562b42fb74a5c25b8fc1fb1cb5", size = 1627695, upload-time = "2025-05-17T17:23:17.38Z" },
{ url = "https://files.pythonhosted.org/packages/6a/cf/80f4297a4820dfdfd1c88cf6c4666a200f204b3488103d027b5edd9176ec/pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eca54f4bb349d45afc17e3011ed4264ef1cc9e266699874cdd1349c504e64798", size = 1675772, upload-time = "2025-05-17T17:23:19.202Z" },
{ url = "https://files.pythonhosted.org/packages/d1/42/1e969ee0ad19fe3134b0e1b856c39bd0b70d47a4d0e81c2a8b05727394c9/pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2596e643d4365e14d0879dc5aafe6355616c61c2176009270f3048f6d9a61f", size = 1668083, upload-time = "2025-05-17T17:23:21.867Z" },
{ url = "https://files.pythonhosted.org/packages/6e/c3/1de4f7631fea8a992a44ba632aa40e0008764c0fb9bf2854b0acf78c2cf2/pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdfac7cda115bca3a5abb2f9e43bc2fb66c2b65ab074913643803ca7083a79ea", size = 1706056, upload-time = "2025-05-17T17:23:24.031Z" },
{ url = "https://files.pythonhosted.org/packages/f2/5f/af7da8e6f1e42b52f44a24d08b8e4c726207434e2593732d39e7af5e7256/pycryptodomex-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:14c37aaece158d0ace436f76a7bb19093db3b4deade9797abfc39ec6cd6cc2fe", size = 1806478, upload-time = "2025-05-17T17:23:26.066Z" },
]
[[package]]
@@ -496,19 +449,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817, upload-time = "2025-04-23T18:30:43.919Z" },
{ url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357, upload-time = "2025-04-23T18:30:46.372Z" },
{ url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011, upload-time = "2025-04-23T18:30:47.591Z" },
{ url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730, upload-time = "2025-04-23T18:30:49.328Z" },
{ url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178, upload-time = "2025-04-23T18:30:50.907Z" },
{ url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462, upload-time = "2025-04-23T18:30:52.083Z" },
{ url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652, upload-time = "2025-04-23T18:30:53.389Z" },
{ url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306, upload-time = "2025-04-23T18:30:54.661Z" },
{ url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720, upload-time = "2025-04-23T18:30:56.11Z" },
{ url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915, upload-time = "2025-04-23T18:30:57.501Z" },
{ url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884, upload-time = "2025-04-23T18:30:58.867Z" },
{ url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496, upload-time = "2025-04-23T18:31:00.078Z" },
{ url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019, upload-time = "2025-04-23T18:31:01.335Z" },
{ url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" },
{ url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" },
{ url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" },
@@ -554,15 +494,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" },
{ url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" },
{ url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" },
{ url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982, upload-time = "2025-04-23T18:32:53.14Z" },
{ url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412, upload-time = "2025-04-23T18:32:55.52Z" },
{ url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749, upload-time = "2025-04-23T18:32:57.546Z" },
{ url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527, upload-time = "2025-04-23T18:32:59.771Z" },
{ url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225, upload-time = "2025-04-23T18:33:04.51Z" },
{ url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490, upload-time = "2025-04-23T18:33:06.391Z" },
{ url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525, upload-time = "2025-04-23T18:33:08.44Z" },
{ url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446, upload-time = "2025-04-23T18:33:10.313Z" },
{ url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678, upload-time = "2025-04-23T18:33:12.224Z" },
{ url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" },
{ url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" },
{ url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" },
@@ -799,7 +730,6 @@ version = "11.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e8/e9/0b85c81e2b441267bca707b5d89f56c2f02578ef8f3eafddf0e0c0b8848c/pyobjc_core-11.1.tar.gz", hash = "sha256:b63d4d90c5df7e762f34739b39cc55bc63dbcf9fb2fb3f2671e528488c7a87fe", size = 974602, upload-time = "2025-06-14T20:56:34.189Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a5/c5/9fa74ef6b83924e657c5098d37b36b66d1e16d13bc45c44248c6248e7117/pyobjc_core-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4c7536f3e94de0a3eae6bb382d75f1219280aa867cdf37beef39d9e7d580173c", size = 676323, upload-time = "2025-06-14T20:44:44.675Z" },
{ url = "https://files.pythonhosted.org/packages/5a/a7/55afc166d89e3fcd87966f48f8bca3305a3a2d7c62100715b9ffa7153a90/pyobjc_core-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ec36680b5c14e2f73d432b03ba7c1457dc6ca70fa59fd7daea1073f2b4157d33", size = 671075, upload-time = "2025-06-14T20:44:46.594Z" },
{ url = "https://files.pythonhosted.org/packages/c0/09/e83228e878e73bf756749939f906a872da54488f18d75658afa7f1abbab1/pyobjc_core-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:765b97dea6b87ec4612b3212258024d8496ea23517c95a1c5f0735f96b7fd529", size = 677985, upload-time = "2025-06-14T20:44:48.375Z" },
{ url = "https://files.pythonhosted.org/packages/c5/24/12e4e2dae5f85fd0c0b696404ed3374ea6ca398e7db886d4f1322eb30799/pyobjc_core-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:18986f83998fbd5d3f56d8a8428b2f3e0754fd15cef3ef786ca0d29619024f2c", size = 676431, upload-time = "2025-06-14T20:44:49.908Z" },
@@ -819,7 +749,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/78/b4/10c16e9d48568a68da2f61866b19468d4ac7129c377d4b1333ee936ae5d0/pyobjc_framework_accessibility-11.1.tar.gz", hash = "sha256:c0fa5f1e00906ec002f582c7d3d80463a46d19f672bf5ec51144f819eeb40656", size = 45098, upload-time = "2025-06-14T20:56:35.287Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f1/ac/ab730d9fe246935e2504cb7e7b673327f16c3c5429f86fb072582ecb4496/pyobjc_framework_accessibility-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8bbe921650607461fcaba6cfb921e8cb0d301e870553fb5353d7f1787a355696", size = 11129, upload-time = "2025-06-14T20:44:57.459Z" },
{ url = "https://files.pythonhosted.org/packages/ff/c5/8803e4f9c3f2d3f5672097438e305be9ccfb87ad092c68cbf02b172bf1d2/pyobjc_framework_accessibility-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:332263153d829b946b311ddc8b9a4402b52d40a572b44c69c3242451ced1b008", size = 11135, upload-time = "2025-06-14T20:44:58.339Z" },
{ url = "https://files.pythonhosted.org/packages/5d/bd/087d511e0ea356434399609a38e8819978943cbeaca3ca7cc5f35c93d0b2/pyobjc_framework_accessibility-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a049b63b32514da68aaaeef0d6c00a125e0618e4042aa6dbe3867b74fb2a8b2b", size = 11158, upload-time = "2025-06-14T20:44:59.032Z" },
{ url = "https://files.pythonhosted.org/packages/0e/1e/4095d683954401d5f7926827fd09f4d399a8923e0e66d386a8903c0950e0/pyobjc_framework_accessibility-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fd5a03b731d1a2bbb2bf706b58889a5e82df82ac69210ec3245c7dc69e42a63a", size = 11177, upload-time = "2025-06-14T20:45:00.111Z" },
@@ -851,7 +780,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/eb/d3/f5bb5c72be5c6e52224f43e23e5a44e86d2c35ee9af36939e5514c6c7a0f/pyobjc_framework_addressbook-11.1.tar.gz", hash = "sha256:ce2db3be4a3128bf79d5c41319a6d16b73754785ce75ac694d0d658c690922fc", size = 97609, upload-time = "2025-06-14T20:56:37.324Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5e/1f/ba28a963bc38a58493e7f22c0a25c7ccca415d7a0aa4fa8aeef2843d775a/pyobjc_framework_addressbook-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:013db030aebe7c09752492ed8f9b12ff41b1264ed119e9858241d57276961e75", size = 13153, upload-time = "2025-06-14T20:45:05.867Z" },
{ url = "https://files.pythonhosted.org/packages/8f/46/27ade210b0bcf2903540c37e96f5e88ec5303e98dc12b255148f12ef9c04/pyobjc_framework_addressbook-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d1d69330b5a87a29d26feea95dcf40681fd00ba3b40ac89579072ce536b6b647", size = 13156, upload-time = "2025-06-14T20:45:06.788Z" },
{ url = "https://files.pythonhosted.org/packages/c2/de/e1ba5f113c05b543a097040add795fa4b85fdd5ad850b56d83cd6ce8afff/pyobjc_framework_addressbook-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fb3d0a710f8342a0c63a8e4caf64a044b4d7e42d6d242c8e1b54470238b938cb", size = 13173, upload-time = "2025-06-14T20:45:07.755Z" },
{ url = "https://files.pythonhosted.org/packages/59/53/a0487a0fbc9134e69e29f18334d0b610c44578d753e8264ea1ac649f2839/pyobjc_framework_addressbook-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:411adf4874cc4343f2928a26fe4cb3673d2f5f73365b45cd3650aa7304a45e24", size = 13188, upload-time = "2025-06-14T20:45:08.811Z" },
@@ -924,7 +852,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/be/3f/b33ce0cecc3a42f6c289dcbf9ff698b0d9e85f5796db2e9cb5dadccffbb9/pyobjc_framework_applicationservices-11.1.tar.gz", hash = "sha256:03fcd8c0c600db98fa8b85eb7b3bc31491701720c795e3f762b54e865138bbaf", size = 224842, upload-time = "2025-06-14T20:56:40.648Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d9/2b/b46566639b13354d348092f932b4debda2e8604c9b1b416eb3619676e997/pyobjc_framework_applicationservices-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:89aa713f16f1de66efd82f3be77c632ad1068e51e0ef0c2b0237ac7c7f580814", size = 30991, upload-time = "2025-06-14T20:45:17.223Z" },
{ url = "https://files.pythonhosted.org/packages/39/2d/9fde6de0b2a95fbb3d77ba11b3cc4f289dd208f38cb3a28389add87c0f44/pyobjc_framework_applicationservices-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cf45d15eddae36dec2330a9992fc852476b61c8f529874b9ec2805c768a75482", size = 30991, upload-time = "2025-06-14T20:45:18.169Z" },
{ url = "https://files.pythonhosted.org/packages/38/ec/46a5c710e2d7edf55105223c34fed5a7b7cc7aba7d00a3a7b0405d6a2d1a/pyobjc_framework_applicationservices-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f4a85ccd78bab84f7f05ac65ff9be117839dfc09d48c39edd65c617ed73eb01c", size = 31056, upload-time = "2025-06-14T20:45:18.925Z" },
{ url = "https://files.pythonhosted.org/packages/c4/06/c2a309e6f37bfa73a2a581d3301321b2033e25b249e2a01e417a3c34e799/pyobjc_framework_applicationservices-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:385a89f4d0838c97a331e247519d9e9745aa3f7427169d18570e3c664076a63c", size = 31072, upload-time = "2025-06-14T20:45:19.707Z" },
@@ -956,7 +883,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/c3/25/6c5a7b1443d30139cc722029880284ea9dfa575f0436471b9364fcd499f5/pyobjc_framework_audiovideobridging-11.1.tar.gz", hash = "sha256:12756b3aa35083b8ad5c9139b6a0e2f4792e217096b5bf6b702d499038203991", size = 72913, upload-time = "2025-06-14T20:56:42.128Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/49/06/f160739013cc43744aa1b110bd2494472fde587056b8910b01b191eb60a2/pyobjc_framework_audiovideobridging-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dd88f7083cc7858c21bfc151a9745e6c24d4f4fa1c3ad5a50673f34c42e17111", size = 11021, upload-time = "2025-06-14T20:45:24.665Z" },
{ url = "https://files.pythonhosted.org/packages/3f/d0/952ccd59944f98f10f39c061ef7c3dceecbcd2654910e763c0ad2fd1c910/pyobjc_framework_audiovideobridging-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:db570433910d1df49cc45d25f7a966227033c794fb41133d59212689b86b1ac6", size = 11021, upload-time = "2025-06-14T20:45:25.498Z" },
{ url = "https://files.pythonhosted.org/packages/1d/69/3e8e3da4db835168d18155a2c90fcca441047fc9c2e021d2ea01b4c6eb8c/pyobjc_framework_audiovideobridging-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:591e80ff6973ea51a12f7c1a2e3fd59496633a51d5a1bf73f4fb989a43e23681", size = 11032, upload-time = "2025-06-14T20:45:26.196Z" },
{ url = "https://files.pythonhosted.org/packages/0b/93/cf38f503f378e224a57f99f8ca7f044f2690221dc8deaf49b305a6ee439a/pyobjc_framework_audiovideobridging-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:30a12be3784f41e1c6b5ef532c08e73bae7071d9a036b26b1e36b919ee5b6f57", size = 11043, upload-time = "2025-06-14T20:45:27.214Z" },
@@ -975,7 +901,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/8f/b7/3e9ad0ed3625dc02e495615ea5dbf55ca95cbd25b3e31f25092f5caad640/pyobjc_framework_authenticationservices-11.1.tar.gz", hash = "sha256:8fd801cdb53d426b4e678b0a8529c005d0c44f5a17ccd7052a7c3a1a87caed6a", size = 115266, upload-time = "2025-06-14T20:56:42.889Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/61/fe/71295a7b5784a8039aec946c1a1dc1f68743c068d8682d5d3478bb216c3e/pyobjc_framework_authenticationservices-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4454c2f69c04fc31c0ec0924ccb3aa9bfe8a11d5632d83172904b5b4cc34d8b5", size = 20284, upload-time = "2025-06-14T20:45:31.412Z" },
{ url = "https://files.pythonhosted.org/packages/31/99/0a9d2b9c1aa3b9713d322ddb90a59537013afdae5661af233409e7a24dc9/pyobjc_framework_authenticationservices-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3987b7fc9493c2ba77b773df99f6631bff1ee9b957d99e34afa6b4e1c9d48bfb", size = 20280, upload-time = "2025-06-14T20:45:32.617Z" },
{ url = "https://files.pythonhosted.org/packages/7e/2d/cbb5e88c3713fb68cda7d76d37737076c1653bf1ac95418c30d4b614f4be/pyobjc_framework_authenticationservices-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6655dd53d9135ef85265a4297da5e7459ed7836973f2796027fdfbfd7f08e433", size = 20385, upload-time = "2025-06-14T20:45:33.359Z" },
{ url = "https://files.pythonhosted.org/packages/53/ac/cfd8aed9fba6974f291b3beb198c7270e4a3cae9f1ff9600bd0e4c904ae9/pyobjc_framework_authenticationservices-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:364035d265129192e6906f7a94cbdf714d737b6b9f20e56bfe74d0007c8761b1", size = 20401, upload-time = "2025-06-14T20:45:34.114Z" },
@@ -994,7 +919,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/3d/39/d4c94e0245d290b83919854c4f205851cc0b2603f843448fdfb8e74aad71/pyobjc_framework_automaticassessmentconfiguration-11.1.tar.gz", hash = "sha256:70eadbf8600101901a56fcd7014d8941604e14f3b3728bc4fb0178a9a9420032", size = 24933, upload-time = "2025-06-14T20:56:43.984Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/23/df/a2dfb08bd2b2984c041890569bd5553dcc304c42f6b11294130c5a32030a/pyobjc_framework_automaticassessmentconfiguration-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a88e75b600c570939190795ead28e097c62aa040467088352c550df52d96e8d4", size = 9172, upload-time = "2025-06-14T20:45:38.201Z" },
{ url = "https://files.pythonhosted.org/packages/b0/ca/f4ee1c9c274e0a41f8885f842fc78e520a367437edf9ca86eca46709e62d/pyobjc_framework_automaticassessmentconfiguration-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:50cc5466bec1f58f79921d49544b525b56897cb985dfcfabf825ee231c27bcfc", size = 9167, upload-time = "2025-06-14T20:45:39.52Z" },
{ url = "https://files.pythonhosted.org/packages/5e/e0/5a67f8ee0393447ca8251cbd06788cb7f3a1f4b9b052afd2e1b2cdfcb504/pyobjc_framework_automaticassessmentconfiguration-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:55d1684dd676730fb1afbc7c67e0669e3a7159f18c126fea7453fe6182c098f9", size = 9193, upload-time = "2025-06-14T20:45:40.52Z" },
{ url = "https://files.pythonhosted.org/packages/58/04/e2fb203d36b7ec96b06ef26cb44b833d64195435bc5d879987238111b524/pyobjc_framework_automaticassessmentconfiguration-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fbcbe406c2a02d632885f6b23285c259b715f019b938d666cc554a66ecf5f9c3", size = 9199, upload-time = "2025-06-14T20:45:41.742Z" },
@@ -1013,7 +937,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/63/9f/097ed9f4de9e9491a1b08bb7d85d35a95d726c9e9f5f5bf203b359a436b6/pyobjc_framework_automator-11.1.tar.gz", hash = "sha256:9b46c55a4f9ae2b3c39ff560f42ced66bdd18c093188f0b5fc4060ad911838e4", size = 201439, upload-time = "2025-06-14T20:56:44.767Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/bb/8a/4a8a4c57fb73d095f486092ab496e5f949eb840cc7bb358fed5c232dc756/pyobjc_framework_automator-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:569f9fedcd107721c59eccce89c5befe429baace59616f9f1ceeb9689a65f273", size = 10004, upload-time = "2025-06-14T20:45:45.299Z" },
{ url = "https://files.pythonhosted.org/packages/f0/c0/ebcc5a041440625ca984cde4ff96bc3e2cac4e5a37ca5bf4506ef4a98c54/pyobjc_framework_automator-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bf675a19edd97de9c19dcfd0fea9af9ebbd3409786c162670d1d71cb2738e341", size = 10004, upload-time = "2025-06-14T20:45:46.111Z" },
{ url = "https://files.pythonhosted.org/packages/0e/1e/3ed1df2168e596151da2329258951dae334e194d7de3b117c7e29a768ffc/pyobjc_framework_automator-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:af5941f8d90167244209b352512b7779e5590d17dc1e703e087a6cfe79ee3d64", size = 10029, upload-time = "2025-06-14T20:45:46.823Z" },
{ url = "https://files.pythonhosted.org/packages/25/ed/a92cea530aac0cf08287321ec8123e8447f93461521f46bb329058b322eb/pyobjc_framework_automator-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3458f836671ea922ad0771f617c927e9c52841c0a6e71b4a5a9dbb438736c207", size = 10040, upload-time = "2025-06-14T20:45:47.549Z" },
@@ -1035,7 +958,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/3c/1f/90cdbce1d3b4861cbb17c12adf57daeec32477eb1df8d3f9ab8551bdadfb/pyobjc_framework_avfoundation-11.1.tar.gz", hash = "sha256:6663056cc6ca49af8de6d36a7fff498f51e1a9a7f1bde7afba718a8ceaaa7377", size = 832178, upload-time = "2025-06-14T20:56:46.329Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5a/b3/be739115eebb03ea38853ad2dcedf8a21da923415b0b6b4dc6f50c737863/pyobjc_framework_avfoundation-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:09542590d1f3aa96d4d1a37712b98fd9657e250d9ea06ecdf2a8a59c837a2cb6", size = 70713, upload-time = "2025-06-14T20:45:51.427Z" },
{ url = "https://files.pythonhosted.org/packages/0f/48/31286b2b09a619d8047256d7180e0d511be71ab598e5f54f034977b59bbf/pyobjc_framework_avfoundation-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8a0ccbdba46b69dec1d12eea52eef56fcd63c492f73e41011bb72508b2aa2d0e", size = 70711, upload-time = "2025-06-14T20:45:52.461Z" },
{ url = "https://files.pythonhosted.org/packages/43/30/d5d03dd4a508bdaa2156ff379e9e109020de23cbb6316c5865d341aa6db1/pyobjc_framework_avfoundation-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:94f065db4e87b1baebb5cf9f464cf9d82c5f903fff192001ebc974d9e3132c7e", size = 70746, upload-time = "2025-06-14T20:45:53.253Z" },
{ url = "https://files.pythonhosted.org/packages/3f/8c/b8ced7700b0e931dc37d14b05e2bead28d2598c887832b3d697da55b1845/pyobjc_framework_avfoundation-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e204d155a09c186601490e4402dcffb2845a5831079e389b47bd6a341fe5ee63", size = 70773, upload-time = "2025-06-14T20:45:54.059Z" },
@@ -1055,7 +977,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/61/ff/9f41f2b8de786871184b48c4e5052cb7c9fcc204e7fee06687fa32b08bed/pyobjc_framework_avkit-11.1.tar.gz", hash = "sha256:d948204a7b94e0e878b19a909f9b33342e19d9ea519571d66a21fce8f72e3263", size = 46825, upload-time = "2025-06-14T20:56:47.494Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/9f/bb5d6f3374e92246acfe25ed6f0ef08b13722c368ef1066893a9f5a6cddd/pyobjc_framework_avkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e211c8dce60b7dd7ad2994ad404041a50e183a039b253f891dbb8cab48f5e687", size = 11518, upload-time = "2025-06-14T20:45:58.783Z" },
{ url = "https://files.pythonhosted.org/packages/a4/6c/ee7504367f4a9337d3e78cd34beb9fcb58ad30e274c2a9f1d8058b9837f2/pyobjc_framework_avkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:88f70e2a399e43ce7bc3124b3b35d65537daddb358ea542fbb0146fa6406be8a", size = 11517, upload-time = "2025-06-14T20:45:59.676Z" },
{ url = "https://files.pythonhosted.org/packages/b2/2f/6ec6a4ec7eb9ca329f36bbd2a51750fe5064d44dd437d8615abb7121ec93/pyobjc_framework_avkit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ef9cd9fe37c6199bfde7ee5cd6e76ede23a6797932882785c53ef3070e209afb", size = 11539, upload-time = "2025-06-14T20:46:00.375Z" },
{ url = "https://files.pythonhosted.org/packages/16/c8/6f0131f62f70e201a605b762cc05804b01fd493a7f21824d714140b7fd99/pyobjc_framework_avkit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c5810b349745078ef8b4a562e85afe40de3245127f633d8cabe98aeca765c7fc", size = 11551, upload-time = "2025-06-14T20:46:01.071Z" },
@@ -1074,7 +995,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/cf/42/94bc18b968a4ee8b6427257f907ffbfc97f8ba6a6202953da149b649d638/pyobjc_framework_avrouting-11.1.tar.gz", hash = "sha256:7db1291d9f53cc58d34b2a826feb721a85f50ceb5e71952e8762baacd3db3fc0", size = 21069, upload-time = "2025-06-14T20:56:48.57Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/27/b1/cdffeb94bf2e3bd4f481e6278d2cbec636693ed8e5a505752a4c65a8c47d/pyobjc_framework_avrouting-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:230daf3e5135f6ad0ab0acd6cf3a01a4b0d6b07eb82d63d6e8037479b6cac4ea", size = 8192, upload-time = "2025-06-14T20:46:04.688Z" },
{ url = "https://files.pythonhosted.org/packages/54/d4/0d17fd5a761d8a3d7dab0e096315de694b47dd48d2bb9655534e44399385/pyobjc_framework_avrouting-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:45cbabbf69764b2467d78adb8f3b7f209d1a8ee690e19f9a32d05c62a9c3a131", size = 8192, upload-time = "2025-06-14T20:46:05.479Z" },
{ url = "https://files.pythonhosted.org/packages/01/17/ce199bc7fb3ba1f7b0474554bd71d1bdd3d5a141e1d9722ff9f46c104e1d/pyobjc_framework_avrouting-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dc309e175abf3961f933f8b341c0504b17f4717931242ebb121a83256b8b5c13", size = 8212, upload-time = "2025-06-14T20:46:06.17Z" },
{ url = "https://files.pythonhosted.org/packages/72/39/5c550da37c6d5a18a9b4a7d0fd6f7396ca8fbbee8cfccf82f3298e0f86b3/pyobjc_framework_avrouting-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f52f9d62a3c8485b5687187ea58d905d7edccac9941c444b4add8129841cd031", size = 8230, upload-time = "2025-06-14T20:46:06.919Z" },
@@ -1093,7 +1013,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/08/76/21e1632a212f997d7a5f26d53eb997951978916858039b79f43ebe3d10b2/pyobjc_framework_backgroundassets-11.1.tar.gz", hash = "sha256:2e14b50539d96d5fca70c49f21b69fdbad81a22549e3630f5e4f20d5c0204fc2", size = 24803, upload-time = "2025-06-14T20:56:49.566Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b5/a9/a163e9c9a14f0005990fa93cd19ab959aa1e2c074d9ca533e943bf9f9f35/pyobjc_framework_backgroundassets-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:30b4fe4b711e1dacf48074f10b8cad680b4e2c422652ae3c32b1f89bb5bac54e", size = 9694, upload-time = "2025-06-14T20:46:11.049Z" },
{ url = "https://files.pythonhosted.org/packages/74/ac/b1cb5c0ec2691ea225d53c2b9411d5ea1896f8f72eb5ca92978664443bb0/pyobjc_framework_backgroundassets-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bd371ce08d1b79f540d5994139898097b83b1d4e4471c264892433d448b24de0", size = 9691, upload-time = "2025-06-14T20:46:12.197Z" },
{ url = "https://files.pythonhosted.org/packages/ad/77/a6ad2df35fd71b3c26f52698d25174899ba1be134766022f5bf804ebf12d/pyobjc_framework_backgroundassets-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:13bf451c59b409b6ce1ac0e717a970a1b03bca7a944a7f19219da0d46ab7c561", size = 9707, upload-time = "2025-06-14T20:46:12.88Z" },
{ url = "https://files.pythonhosted.org/packages/1d/7f/ed035866ab6c0573c445a9ed1ceb0912119866c130df7684a2332642520e/pyobjc_framework_backgroundassets-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:708466d847a479e1798f31c59fbc5307473d03fa1083f40cfcaa18fd31819c40", size = 9722, upload-time = "2025-06-14T20:46:13.574Z" },
@@ -1115,7 +1034,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/30/75/087270d9f81e913b57c7db58eaff8691fa0574b11faf9302340b3b8320f1/pyobjc_framework_browserenginekit-11.1.tar.gz", hash = "sha256:918440cefb10480024f645169de3733e30ede65e41267fa12c7b90c264a0a479", size = 31944, upload-time = "2025-06-14T20:56:50.195Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2b/2f/bf24b06df34233f7a73c62ecd8cb7e4567a33641ee10d2edb31b5eead2d4/pyobjc_framework_browserenginekit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cbfb0183378a0dc836bcdf3798c358c6d217aeaac726e1d44be7cc123994c0fa", size = 11086, upload-time = "2025-06-14T20:46:17.124Z" },
{ url = "https://files.pythonhosted.org/packages/ea/29/ec0a0cc6fb15911769cb8e5ad8ada85e3f5cf4889fafbb90d936c6b7053b/pyobjc_framework_browserenginekit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:29b5f5949170af0235485e79aa465a7af2b2e0913d0c2c9ab1ac033224a90edb", size = 11088, upload-time = "2025-06-14T20:46:18.696Z" },
{ url = "https://files.pythonhosted.org/packages/89/90/a50bb66a5e041ace99b6c8b1df43b38d5f2e1bf771f57409e4aebf1dfae5/pyobjc_framework_browserenginekit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9b815b167533015d62832b956e9cfb962bd2026f5a4ccd66718cf3bb2e15ab27", size = 11115, upload-time = "2025-06-14T20:46:19.401Z" },
{ url = "https://files.pythonhosted.org/packages/44/0a/3cbfc8ca58ed9aeef7498f318ad209164903e64eba1ea94a661a59ee67e6/pyobjc_framework_browserenginekit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dfe469f8eb1313ea0cbe0616cd3bbc56f62bdd8a683c959819ef01d7e9ac0de7", size = 11134, upload-time = "2025-06-14T20:46:20.445Z" },
@@ -1160,7 +1078,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/51/d5/4f0b62ab35be619e8c8d96538a03cf56fde6fd53540e1837e0fa588b3f6c/pyobjc_framework_callkit-11.1.tar.gz", hash = "sha256:b84d5ea38dff0cbe0754f5f9f6f33c742e216f12e7166179a8ec2cf4b0bfca94", size = 46648, upload-time = "2025-06-14T20:56:52.579Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1d/63/2a15991ca949c6d4e352e49799e14c66e94cf824ae87c67d86a58d174bd9/pyobjc_framework_callkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:afa1520c462b571458d0d6139681820b4abd41d7dbd7d4892fb2617dd9037846", size = 11209, upload-time = "2025-06-14T20:46:26.28Z" },
{ url = "https://files.pythonhosted.org/packages/5a/f8/6e368225634cad9e457c4f8f0580ed318cb2f2c8110f2e56935fc12502f3/pyobjc_framework_callkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1db8b74abd6489d73c8619972730bea87a7d1f55d47649150fc1a30fdc6840fb", size = 11211, upload-time = "2025-06-14T20:46:27.146Z" },
{ url = "https://files.pythonhosted.org/packages/18/2a/209572a6dba6768a57667e1f87a83ce8cadf18de5d6b1a91b95ce548d0f8/pyobjc_framework_callkit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:554e09ca3dab44d93a89927d9e300f004d2ef0db020b10425a4622b432e7b684", size = 11269, upload-time = "2025-06-14T20:46:28.164Z" },
{ url = "https://files.pythonhosted.org/packages/8f/74/b0a22adb7ebcd0b81c24ed6e49d3df3b84f73192b667ebd90cb1b6eba917/pyobjc_framework_callkit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fc5e638ddbc9dd3e9993205d2b077f5db41b6cd4e97b9c5592b7249575f23f04", size = 11284, upload-time = "2025-06-14T20:46:29.197Z" },
@@ -1192,7 +1109,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/6f/49/7b24172e3d6eb0ddffc33a7498a2bea264aa2958c3fecaeb463bef88f0b8/pyobjc_framework_cfnetwork-11.1.tar.gz", hash = "sha256:ad600163eeadb7bf71abc51a9b6f2b5462a018d3f9bb1510c5ce3fdf2f22959d", size = 79069, upload-time = "2025-06-14T20:56:54.615Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e1/33/cce0badb0dc67b26aeb885ac454b9a7d60b13e2a75ec388175d983ae5737/pyobjc_framework_cfnetwork-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8cf313e3ac580ee0d3c2345771e6cafc4ba95a10418e3e535feeda4c62b68295", size = 18950, upload-time = "2025-06-14T20:46:34.176Z" },
{ url = "https://files.pythonhosted.org/packages/e7/61/74b0d0430807615b7f91a688a871ffd94a61d4764a101e2a53e0c95dd05e/pyobjc_framework_cfnetwork-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d7a24746d0754b3a0042def2cd64aa205e5614f12ea0de9461c8e26d97633c72", size = 18953, upload-time = "2025-06-14T20:46:35.409Z" },
{ url = "https://files.pythonhosted.org/packages/c2/31/05b4fb79e7f738f7f7d7a58734de2fab47d9a1fb219c2180e8c07efe2550/pyobjc_framework_cfnetwork-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:70beb8095df76e0e8eb7ab218be1e69ae180e01a4d77f7cad73c97b4eb7a296a", size = 19141, upload-time = "2025-06-14T20:46:36.134Z" },
{ url = "https://files.pythonhosted.org/packages/2d/b1/5ea76ffd6413be8c65ec02e4552e3da3ee2bd37449e0854e3c8c559e7e42/pyobjc_framework_cfnetwork-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dd866fcbe6870931373636d19144544344f0f89685f6720e4a45453957702dd", size = 19148, upload-time = "2025-06-14T20:46:36.876Z" },
@@ -1227,7 +1143,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/7a/8b/5150b4faddd15d5dd795bc62b2256c4f7dafc983cfa694fcf88121ea0016/pyobjc_framework_classkit-11.1.tar.gz", hash = "sha256:ee1e26395eb00b3ed5442e3234cdbfe925d2413185af38eca0477d7166651df4", size = 39831, upload-time = "2025-06-14T20:56:56.036Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7b/a7/48ba463a9454a9daa80d349f936e83ebf703857f443edc70aa5970ec77a8/pyobjc_framework_classkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1cb2a2b68fb4c773e9ff250f2ab87c41b7464778d4c7e0174600de3cf5f7b52e", size = 8894, upload-time = "2025-06-14T20:46:41.846Z" },
{ url = "https://files.pythonhosted.org/packages/89/86/5b9ef1d5aa3f4835d164c9be46afae634911db56c6ad7795e212ef9bb50b/pyobjc_framework_classkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:018da363d06f3615c07a8623cbdb024a31b1f8b96a933ff2656c0e903063842c", size = 8895, upload-time = "2025-06-14T20:46:42.689Z" },
{ url = "https://files.pythonhosted.org/packages/75/79/2552fd5e1da73dffb35589469b3cd8c0928e3100462761350d19ea922e59/pyobjc_framework_classkit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:161dcb9b718649e6331a5eab5a76c2b43a9b322b15b37b3f8f9c5faad12ee6d1", size = 8911, upload-time = "2025-06-14T20:46:43.714Z" },
{ url = "https://files.pythonhosted.org/packages/59/1c/a06623c3d78949c9d5eae7c7e753e6c8c75e2ae7a0b8ccae40a1b6180e0a/pyobjc_framework_classkit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:08000deb43004d16fb39ccd83b3de30e1e3b72639a79d05206d7d5c15f005b3a", size = 8928, upload-time = "2025-06-14T20:46:44.426Z" },
@@ -1261,7 +1176,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/4b/c5/7a866d24bc026f79239b74d05e2cf3088b03263da66d53d1b4cf5207f5ae/pyobjc_framework_cocoa-11.1.tar.gz", hash = "sha256:87df76b9b73e7ca699a828ff112564b59251bb9bbe72e610e670a4dc9940d038", size = 5565335, upload-time = "2025-06-14T20:56:59.683Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/87/8f/67a7e166b615feb96385d886c6732dfb90afed565b8b1f34673683d73cd9/pyobjc_framework_cocoa-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b27a5bdb3ab6cdeb998443ff3fce194ffae5f518c6a079b832dbafc4426937f9", size = 388187, upload-time = "2025-06-14T20:46:49.74Z" },
{ url = "https://files.pythonhosted.org/packages/90/43/6841046aa4e257b6276cd23e53cacedfb842ecaf3386bb360fa9cc319aa1/pyobjc_framework_cocoa-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7b9a9b8ba07f5bf84866399e3de2aa311ed1c34d5d2788a995bdbe82cc36cfa0", size = 388177, upload-time = "2025-06-14T20:46:51.454Z" },
{ url = "https://files.pythonhosted.org/packages/68/da/41c0f7edc92ead461cced7e67813e27fa17da3c5da428afdb4086c69d7ba/pyobjc_framework_cocoa-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806de56f06dfba8f301a244cce289d54877c36b4b19818e3b53150eb7c2424d0", size = 388983, upload-time = "2025-06-14T20:46:52.591Z" },
{ url = "https://files.pythonhosted.org/packages/4e/0b/a01477cde2a040f97e226f3e15e5ffd1268fcb6d1d664885a95ba592eca9/pyobjc_framework_cocoa-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:54e93e1d9b0fc41c032582a6f0834befe1d418d73893968f3f450281b11603da", size = 389049, upload-time = "2025-06-14T20:46:53.757Z" },
@@ -1306,7 +1220,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/a6/85/34868b6447d552adf8674bac226b55c2baacacee0d67ee031e33805d6faa/pyobjc_framework_contacts-11.1.tar.gz", hash = "sha256:752036e7d8952a4122296d7772f274170a5f35a53ee6454a27f3e1d9603222cc", size = 84814, upload-time = "2025-06-14T20:57:02.582Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ce/90/e7dc8cddd07783a734b6cc72e37618ce573437213db1ca17948246ce7043/pyobjc_framework_contacts-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:01a83ac9e03cab16ee7eb8755c87ce1790c8465487a07994da5569d843facc05", size = 12064, upload-time = "2025-06-14T20:47:01.332Z" },
{ url = "https://files.pythonhosted.org/packages/68/e1/27715ef476441cb05d4442b93fe6380a57a946cda008f70399cadb4ff1fd/pyobjc_framework_contacts-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:68148653f27c1eaeff2ad4831b5e68393071a382aab773629cd047ce55556726", size = 12067, upload-time = "2025-06-14T20:47:02.178Z" },
{ url = "https://files.pythonhosted.org/packages/30/c8/0d47af11112bf382e059cfe2dd03be98914f0621ddff8858bb9af864f8c5/pyobjc_framework_contacts-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:576ee4aec05d755444bff10b45833f73083b5b3d1b2740e133b92111f7765e54", size = 12141, upload-time = "2025-06-14T20:47:02.884Z" },
{ url = "https://files.pythonhosted.org/packages/11/af/375aa44e9e00aa66e373c4c3893a0db341d93f90e2d62a277287dc553841/pyobjc_framework_contacts-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:09b873d2bd739fea63d744430defb04ce4b44af064aaf0b6bf558eea23f82bd7", size = 12160, upload-time = "2025-06-14T20:47:03.614Z" },
@@ -1326,7 +1239,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/3f/57/8765b54a30edaa2a56df62e11e7c32e41b6ea300513256adffa191689368/pyobjc_framework_contactsui-11.1.tar.gz", hash = "sha256:5bc29ea2b10a342018e1b96be6b140c10ebe3cfb6417278770feef5e88026a1f", size = 20031, upload-time = "2025-06-14T20:57:03.603Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/f9/5f7af5363adf9601b7acf9c84f57cdba1a98193a8ec99dc544a35f8e6b17/pyobjc_framework_contactsui-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cf3468444ba88dd6814e02ffa624d13b7857a7baf042462452bb292337c013da", size = 7858, upload-time = "2025-06-14T20:47:07.121Z" },
{ url = "https://files.pythonhosted.org/packages/38/02/f65f2eb6e2ad91c95e5a6b532fe8dd5cd0c190fbaff71e4a85346e16c0f6/pyobjc_framework_contactsui-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1c0f03c71e63daf5dbf760bf0e45620618a6f1ea62f8c17e288463c1fd4d2685", size = 7858, upload-time = "2025-06-14T20:47:08.346Z" },
{ url = "https://files.pythonhosted.org/packages/46/b6/50ec09f1bb18c422b8c079e02328689f32e977b43ab7651c05e8274854dc/pyobjc_framework_contactsui-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c34a6f27ef5aa4742cc44fd5b4d16fe1e1745ff839578b4c059faf2c58eee3ca", size = 7875, upload-time = "2025-06-14T20:47:09.041Z" },
{ url = "https://files.pythonhosted.org/packages/8b/3f/72170303c11945c360b83fa1c0d3f91638dc5de1ef9f9a2b880252378430/pyobjc_framework_contactsui-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f3b4f0225645a26ed9e6c008c2e8c217035b4a50fa9cd6623c628a11c37924d0", size = 7886, upload-time = "2025-06-14T20:47:09.726Z" },
@@ -1345,7 +1257,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/39/c0/4ab6005cf97e534725b0c14b110d4864b367c282b1c5b0d8f42aad74a83f/pyobjc_framework_coreaudio-11.1.tar.gz", hash = "sha256:b7b89540ae7efc6c1e3208ac838ef2acfc4d2c506dd629d91f6b3b3120e55c1b", size = 141032, upload-time = "2025-06-14T20:57:04.348Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b8/fd/59bcaa6436c27d3da4ea147da4e6f723606317e38e7101f8b191b687176d/pyobjc_framework_coreaudio-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:551c8aac6fdfbd34c3e2d4ce90b36a411e81be20581b978fa4da1a495489792d", size = 35380, upload-time = "2025-06-14T20:47:13.306Z" },
{ url = "https://files.pythonhosted.org/packages/54/1d/81339c1087519a9f125396c717b85a05b49c2c54137bdf4ca01c1ccb6239/pyobjc_framework_coreaudio-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:73a46f0db2fa8ca2e8c47c3ddcc2751e67a0f8600246a6718553b15ee0dbbdb6", size = 35383, upload-time = "2025-06-14T20:47:14.234Z" },
{ url = "https://files.pythonhosted.org/packages/3d/fe/c43521642db98a4ec29fa535781c1316342bb52d5fc709696cbb1e8ca6cd/pyobjc_framework_coreaudio-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2538d1242dab4e27efb346eafbad50594e7e95597fa7220f0bab2099c825da55", size = 36765, upload-time = "2025-06-14T20:47:15.344Z" },
{ url = "https://files.pythonhosted.org/packages/82/9b/24d03ace273585de2d04385f06b895ce92caf8f5af430b060618ebce9dbe/pyobjc_framework_coreaudio-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f73d996df1e721931d9f78050e1708735a173dbe3a76d9c71fb36e04f7208478", size = 36779, upload-time = "2025-06-14T20:47:16.123Z" },
@@ -1365,7 +1276,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/f1/4e/c49b26c60047c511727efe994b412276c487dfe90f1ee0fced0bddbdf8a3/pyobjc_framework_coreaudiokit-11.1.tar.gz", hash = "sha256:0b461c3d6123fda4da6b6aaa022efc918c1de2e126a5cf07d2189d63fa54ba40", size = 21955, upload-time = "2025-06-14T20:57:05.218Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/56/f9/5eb8dbda282901b63d05e1f9fc5cbbb3237de1693de9bf11482defb4d222/pyobjc_framework_coreaudiokit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3be9e254d607324cfc059e3f11fe528fc95d59bb72e585d4bb4ecf92ef493000", size = 7235, upload-time = "2025-06-14T20:47:20.134Z" },
{ url = "https://files.pythonhosted.org/packages/07/44/0de5d26e383d0b00f2f44394db74e0953bc1e35b74072a67fde916e8e31e/pyobjc_framework_coreaudiokit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4743fbd210159cffffb0a7b8e06bf8b8527ba4bf01e76806fae2696fd6990e77", size = 7234, upload-time = "2025-06-14T20:47:21.271Z" },
{ url = "https://files.pythonhosted.org/packages/18/27/d8ff6293851a7d9665724fa5c324d28200776ec10a04b850ba21ad1f9be1/pyobjc_framework_coreaudiokit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:20440a2926b1d91da8efc8bc060e77c7a195cb0443dbf3770eaca9e597276748", size = 7266, upload-time = "2025-06-14T20:47:22.136Z" },
{ url = "https://files.pythonhosted.org/packages/13/e6/89aa525271d19f0ea11799021f364181dd62dbfe77ecb4fc0a7d4e579cd2/pyobjc_framework_coreaudiokit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11d42770dfbc6a8af8d5fa39a4f700f0067d7e6c7ba9335e6624d89de3c599a9", size = 7273, upload-time = "2025-06-14T20:47:23.137Z" },
@@ -1384,7 +1294,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/3d/fe/2081dfd9413b7b4d719935c33762fbed9cce9dc06430f322d1e2c9dbcd91/pyobjc_framework_corebluetooth-11.1.tar.gz", hash = "sha256:1deba46e3fcaf5e1c314f4bbafb77d9fe49ec248c493ad00d8aff2df212d6190", size = 60337, upload-time = "2025-06-14T20:57:05.919Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/82/93/5b5ec131a238238ac1190758ccc5731b127e05e94a46abd08c5e1094cab9/pyobjc_framework_corebluetooth-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ab509994503a5f0ec0f446a7ccc9f9a672d5a427d40dba4563dd00e8e17dfb06", size = 13140, upload-time = "2025-06-14T20:47:27.457Z" },
{ url = "https://files.pythonhosted.org/packages/8c/75/3318e85b7328c99c752e40592a907fc5c755cddc6d73beacbb432f6aa2d0/pyobjc_framework_corebluetooth-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:433b8593eb1ea8b6262b243ec903e1de4434b768ce103ebe15aac249b890cc2a", size = 13143, upload-time = "2025-06-14T20:47:28.889Z" },
{ url = "https://files.pythonhosted.org/packages/8a/bc/083ea1ae57a31645df7fad59921528f6690995f7b7c84a203399ded7e7fe/pyobjc_framework_corebluetooth-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:36bef95a822c68b72f505cf909913affd61a15b56eeaeafea7302d35a82f4f05", size = 13163, upload-time = "2025-06-14T20:47:29.624Z" },
{ url = "https://files.pythonhosted.org/packages/3e/b5/d07cfa229e3fa0cd1cdaa385774c41907941d25b693cf55ad92e8584a3b3/pyobjc_framework_corebluetooth-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:992404b03033ecf637e9174caed70cb22fd1be2a98c16faa699217678e62a5c7", size = 13179, upload-time = "2025-06-14T20:47:30.376Z" },
@@ -1403,7 +1312,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/00/e3/af497da7a7c895b6ff529d709d855a783f34afcc4b87ab57a1a2afb3f876/pyobjc_framework_coredata-11.1.tar.gz", hash = "sha256:fe9fd985f8e06c70c0fb1e6bbea5b731461f9e76f8f8d8e89c7c72667cdc6adf", size = 260628, upload-time = "2025-06-14T20:57:06.729Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d4/e6/194ac1dca68171b22e38512ec5987c0e147973d1371ac4fd23be71b80f8d/pyobjc_framework_coredata-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ceeba4f9d156610f17e643fc8bdf40bd785bda92fad6f4cbf0954894aa4db165", size = 16427, upload-time = "2025-06-14T20:47:34.105Z" },
{ url = "https://files.pythonhosted.org/packages/29/d9/7f12bdba0503d0ef7b1ac26e05429aecc33b4aaf190155a6bec8b576850d/pyobjc_framework_coredata-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c66ae04cc658eafdfb987f9705e21f9782edee6773a8adb6bfa190500e4e7e29", size = 16428, upload-time = "2025-06-14T20:47:34.981Z" },
{ url = "https://files.pythonhosted.org/packages/5b/ac/77935aa9891bd6be952b1e6780df2bae748971dd0fe0b5155894004840bd/pyobjc_framework_coredata-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c9b2374784e67694a18fc8c120a12f11b355a20b643c01f23ae2ce87330a75e0", size = 16443, upload-time = "2025-06-14T20:47:35.711Z" },
{ url = "https://files.pythonhosted.org/packages/75/50/17631c3f172d9681faad210b035fa3d2c01f59468b574dbc088512853cc2/pyobjc_framework_coredata-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:007160eb10bb8c789076f231e3d625d8875ca42eb5a806fdab5d0277c48866f8", size = 16457, upload-time = "2025-06-14T20:47:36.439Z" },
@@ -1435,7 +1343,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/95/ef/fbd2e01ec137208af7bfefe222773748d27f16f845b0efa950d65e2bd719/pyobjc_framework_corelocation-11.1.tar.gz", hash = "sha256:46a67b99925ee3d53914331759c6ee110b31bb790b74b05915acfca41074c206", size = 104508, upload-time = "2025-06-14T20:57:08.731Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/be/3b/584df38954725bbaad1d45f9702f9fde88ab85d801f2d3ea20657af1816f/pyobjc_framework_corelocation-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:90d7811a2b730f604b0a2ac54c3c822e6e048287e2cd1db80fd3bd1caac6c1c0", size = 12741, upload-time = "2025-06-14T20:47:41.378Z" },
{ url = "https://files.pythonhosted.org/packages/f9/f9/8137e8bf86f8e6350298217dcc635fd6577d64b484f9d250ddb85a84efa0/pyobjc_framework_corelocation-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ea261e7d87c6f62f1b03c252c273ea7fd6f314e3e73c69c6fb3fe807bf183462", size = 12741, upload-time = "2025-06-14T20:47:42.583Z" },
{ url = "https://files.pythonhosted.org/packages/95/cb/282d59421cdb89a5e5fcce72fc37d6eeace98a2a86d71f3be3cd47801298/pyobjc_framework_corelocation-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:562e31124f80207becfd8df01868f73fa5aa70169cc4460e1209fb16916e4fb4", size = 12752, upload-time = "2025-06-14T20:47:43.273Z" },
{ url = "https://files.pythonhosted.org/packages/de/cb/c4672fcfa5e998cfd0dd165717ec312f7e6cbac06ecb4a0e227dbc4d7e27/pyobjc_framework_corelocation-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0f8182835429118a55ed65963c80f5b2892d190747b986e8395b1cd99f41a1d0", size = 12768, upload-time = "2025-06-14T20:47:43.987Z" },
@@ -1454,7 +1361,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/95/5d/81513acd219df77a89176f1574d936b81ad6f6002225cabb64d55efb7e8d/pyobjc_framework_coremedia-11.1.tar.gz", hash = "sha256:82cdc087f61e21b761e677ea618a575d4c0dbe00e98230bf9cea540cff931db3", size = 216389, upload-time = "2025-06-14T20:57:09.546Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/09/5e/396621c2b29353a3cb6d2caa4116583bf1073aedaf2ef196fec85d983696/pyobjc_framework_coremedia-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91231957d25b6d191983166cf218189b5a01e267dadde35eb3a4c359dc473ccb", size = 29113, upload-time = "2025-06-14T20:47:47.978Z" },
{ url = "https://files.pythonhosted.org/packages/32/48/811ccea77d2c0d8156a489e2900298502eb6648d9c041c7f0c514c8f8a29/pyobjc_framework_coremedia-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aacf47006e1c6bf6124fb2b5016a8d5fd5cf504b6b488f9eba4e389ab0f0a051", size = 29118, upload-time = "2025-06-14T20:47:48.895Z" },
{ url = "https://files.pythonhosted.org/packages/2c/d1/b3d004d6a2d2188d196779d92fe8cfa2533f5722cd216fbc4f0cffc63b24/pyobjc_framework_coremedia-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ea5055298af91e463ffa7977d573530f9bada57b8f2968dcc76a75e339b9a598", size = 29015, upload-time = "2025-06-14T20:47:49.655Z" },
{ url = "https://files.pythonhosted.org/packages/1c/23/cafd29011d14eac27fc55770157ebb8e02ffed9f75e01f24e97616417c4c/pyobjc_framework_coremedia-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7ecdb64c743ffe9fd3949c7cc9109891b9f399a0852717fcb969d33c4e7ba527", size = 29031, upload-time = "2025-06-14T20:47:50.395Z" },
@@ -1473,7 +1379,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/64/68/9cef2aefba8e69916049ff43120e8794df8051bdf1f690a55994bbe4eb57/pyobjc_framework_coremediaio-11.1.tar.gz", hash = "sha256:bccd69712578b177144ded398f4695d71a765ef61204da51a21f0c90b4ad4c64", size = 108326, upload-time = "2025-06-14T20:57:10.435Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f6/ce/b4b3aaebcf9511b5f1257b952db6b838d68b83a1f5b2ebbb5b3e4e0e8e05/pyobjc_framework_coremediaio-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:49120679162416ad5a4cf67b49830cf3d38b60bd94496e2a4cad3895496b558d", size = 17205, upload-time = "2025-06-14T20:47:54.902Z" },
{ url = "https://files.pythonhosted.org/packages/aa/38/6bcddd7d57fa19173621aa29b46342756ed1a081103d24e4bdac1cf882fe/pyobjc_framework_coremediaio-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4438713ee4611d5310f4f2e71e557b6138bc79c0363e3d45ecb8c09227dfa58e", size = 17203, upload-time = "2025-06-14T20:47:55.781Z" },
{ url = "https://files.pythonhosted.org/packages/4b/b5/5dd941c1d7020a78b563a213fb8be7c6c3c1073c488914e158cd5417f4f7/pyobjc_framework_coremediaio-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:39ad2518de9943c474e5ca0037e78f92423c3352deeee6c513a489016dac1266", size = 17250, upload-time = "2025-06-14T20:47:56.505Z" },
{ url = "https://files.pythonhosted.org/packages/08/44/cd98e1dacdd28c4e80fe1b0dde3a5171494735cb4a7b8b5775825b824b96/pyobjc_framework_coremediaio-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9e0a079fe790ce8a69d11bea46b315c9a0d3f3999a2f09e2ef4fcc4430a47c42", size = 17226, upload-time = "2025-06-14T20:47:57.267Z" },
@@ -1492,7 +1397,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/06/ca/2ae5149966ccd78290444f88fa62022e2b96ed2fddd47e71d9fd249a9f82/pyobjc_framework_coremidi-11.1.tar.gz", hash = "sha256:095030c59d50c23aa53608777102bc88744ff8b10dfb57afe24b428dcd12e376", size = 107817, upload-time = "2025-06-14T20:57:11.245Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8d/ed/641612dcaf397b86f1e11a960f38193a5962bf207cbe40ce1157cd79b590/pyobjc_framework_coremidi-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5dbd846a2c3f23795a49f363c1e22f0dd4d91ac675f9d52fb5ba93a2bd212d1f", size = 24249, upload-time = "2025-06-14T20:48:01.567Z" },
{ url = "https://files.pythonhosted.org/packages/37/fc/db75c55e492e5e34be637da2eeeaadbb579655b6d17159de419237bc9bdf/pyobjc_framework_coremidi-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5f8c2fdc9d1b7967e2a5ec0d5281eaddc00477bed9753aa14d5b881dc3a9ad8f", size = 24256, upload-time = "2025-06-14T20:48:02.448Z" },
{ url = "https://files.pythonhosted.org/packages/c2/2d/57c279dd74a9073d1416b10b05ebb9598f4868cad010d87f46ef4b517324/pyobjc_framework_coremidi-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:deb9120478a831a898f22f68737fc683bb9b937e77556e78b75986aebd349c55", size = 24277, upload-time = "2025-06-14T20:48:03.184Z" },
{ url = "https://files.pythonhosted.org/packages/1e/66/dfdc7a5dc5a44b1660015bb24454ca0cbdf436e631e39917c495475dbb24/pyobjc_framework_coremidi-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c2e1ab122501206ceae07123fdc433e91a5f1a97224f80ece0717b6f36ad2029", size = 24308, upload-time = "2025-06-14T20:48:04.285Z" },
@@ -1511,7 +1415,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/0d/5d/4309f220981d769b1a2f0dcb2c5c104490d31389a8ebea67e5595ce1cb74/pyobjc_framework_coreml-11.1.tar.gz", hash = "sha256:775923eefb9eac2e389c0821b10564372de8057cea89f1ea1cdaf04996c970a7", size = 82005, upload-time = "2025-06-14T20:57:12.004Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4d/98/390aabc69ac5dd210b4b67dbe24233022222ef4646b5b61f72c775c0574a/pyobjc_framework_coreml-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b1b1b849ca91e0d62ed6dfd200d95ca8d023d6edff854aae77ba54eb0542415f", size = 11415, upload-time = "2025-06-14T20:48:08.367Z" },
{ url = "https://files.pythonhosted.org/packages/76/9c/2218a8f457f56075a8a3f2490bd9d01c8e69c807139eaa0a6ac570531ca5/pyobjc_framework_coreml-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b5be7889ad99da1aca040238fd99af9ee87ea8a6628f24d33e2e4890b88dd139", size = 11414, upload-time = "2025-06-14T20:48:09.267Z" },
{ url = "https://files.pythonhosted.org/packages/3e/9e/a1b6d30b4f91c7cc4780e745e1e73a322bd3524a773f66f5eac0b1600d85/pyobjc_framework_coreml-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c768b03d72488b964d753392e9c587684961d8237b69cca848b3a5a00aea79c9", size = 11436, upload-time = "2025-06-14T20:48:10.048Z" },
{ url = "https://files.pythonhosted.org/packages/95/95/f8739958ccf7cbaaf172653b3665cfcee406c5503a49828130b618b93d3f/pyobjc_framework_coreml-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:10d51f8a5fe8d30c7ec70304a2324df76b48b9fbef30ee0f0c33b99a49ae8853", size = 11452, upload-time = "2025-06-14T20:48:10.74Z" },
@@ -1530,7 +1433,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/a5/95/e469dc7100ea6b9c29a074a4f713d78b32a78d7ec5498c25c83a56744fc2/pyobjc_framework_coremotion-11.1.tar.gz", hash = "sha256:5884a568521c0836fac39d46683a4dea3d259a23837920897042ffb922d9ac3e", size = 67050, upload-time = "2025-06-14T20:57:12.705Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fd/da/403c6582890f5763851e10c466f768211e35f4392c8b5a731c5decc812f5/pyobjc_framework_coremotion-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:87e642511279c080dd9d0c7b0af3903191a6400a6c3a3caeb54233cb642a6966", size = 10353, upload-time = "2025-06-14T20:48:14.547Z" },
{ url = "https://files.pythonhosted.org/packages/1d/3f/137c983dbccbdbc4a07fb2453e494af938078969bcde9252fbbad0ba939d/pyobjc_framework_coremotion-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:501248a726816e05552d1c1f7e2be2c7305cda792c46905d9aee7079dfad2eea", size = 10353, upload-time = "2025-06-14T20:48:15.365Z" },
{ url = "https://files.pythonhosted.org/packages/e9/17/ffa3cf9fde9df31f3d6ecb38507c61c6d8d81276d0a9116979cafd5a0ab7/pyobjc_framework_coremotion-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8c3b33228a170bf8495508a8923451ec600435c7bff93d7614f19c913baeafd1", size = 10368, upload-time = "2025-06-14T20:48:16.066Z" },
{ url = "https://files.pythonhosted.org/packages/7c/2b/ade312f6bda6c368112bc2151834e664c22ae7d6d1f2ce33347b84ece7fb/pyobjc_framework_coremotion-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ac5302deaab99a7443cad63f125061a90040852d4f8efb58492542a612b2afe3", size = 10393, upload-time = "2025-06-14T20:48:16.784Z" },
@@ -1550,7 +1452,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/a8/a9/141d18019a25776f507992f9e7ffc051ca5a734848d8ea8d848f7c938efc/pyobjc_framework_coreservices-11.1.tar.gz", hash = "sha256:cf8eb5e272c60a96d025313eca26ff2487dcd02c47034cc9db39f6852d077873", size = 1245086, upload-time = "2025-06-14T20:57:13.914Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/eb/21/24cabb96b227f3d01a200e83e2e1ff7d4359c3c938de5474858b4665c9c1/pyobjc_framework_coreservices-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:96578c31035fed361d030b0168ae5fc593aa26aa78f6c9946b8da6007e46e08e", size = 30244, upload-time = "2025-06-14T20:48:20.699Z" },
{ url = "https://files.pythonhosted.org/packages/fc/35/a984b9aace173e92b3509f82afe5e0f8ecddf5cf43bf0c01c803f60a19ce/pyobjc_framework_coreservices-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7260e09a0550d57756ad655f3d3815f21fc3f0386aed014be4b46194c346941", size = 30243, upload-time = "2025-06-14T20:48:21.563Z" },
{ url = "https://files.pythonhosted.org/packages/fa/0f/52827197a1fa1dabefd77803920eaf340f25e0c81944844ab329d511cade/pyobjc_framework_coreservices-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6bd313ec326efd715b4b10c3ebcc9f054e3ee3178be407b97ea225cd871351d2", size = 30252, upload-time = "2025-06-14T20:48:22.657Z" },
{ url = "https://files.pythonhosted.org/packages/9d/dc/8a0414dd81054062a56a54db5c1cbb35c715081c9210ed69d5fed8046ebe/pyobjc_framework_coreservices-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8aee505dca56afc5363d8d0dff0b2d26583a8d0f3ac37674cef86f66c51a2934", size = 30271, upload-time = "2025-06-14T20:48:23.427Z" },
@@ -1569,7 +1470,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/31/c7/b67ebfb63b7ccbfda780d583056d1fd4b610ba3839c8ebe3435b86122c61/pyobjc_framework_corespotlight-11.1.tar.gz", hash = "sha256:4dd363c8d3ff7619659b63dd31400f135b03e32435b5d151459ecdacea14e0f2", size = 87161, upload-time = "2025-06-14T20:57:14.934Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1b/35/7d307ddd0c66c0453be435efe40ab4dc8c09231ecd0e0f89e09be10f56fb/pyobjc_framework_corespotlight-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b2d3ddabf74ef04933eb28b1a1c5ed93748b31e64b9c29d5eb88fafab5605c87", size = 9956, upload-time = "2025-06-14T20:48:27.224Z" },
{ url = "https://files.pythonhosted.org/packages/46/d4/87a87384bbb2e27864d527eb00973a056bae72603e6c581711231f2479fc/pyobjc_framework_corespotlight-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d3c571289ce9107f1ade92ad036633f81355f22f70e8ba82d7335f1757381b89", size = 9954, upload-time = "2025-06-14T20:48:28.065Z" },
{ url = "https://files.pythonhosted.org/packages/b9/f8/06b7edfeabe5b3874485b6e5bbe4a39d9f2e1f44348faa7cb320fbc6f21a/pyobjc_framework_corespotlight-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7cedd3792fe1fe2a8dc65a8ff1f70baf12415a5dc9dc4d88f987059567d7e694", size = 9977, upload-time = "2025-06-14T20:48:28.757Z" },
{ url = "https://files.pythonhosted.org/packages/7d/ce/812ae5a7f97a57abce1b2232280d5838a77d5454e5b05d79c3e654ad7400/pyobjc_framework_corespotlight-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:546d0d9b101de4ca20449f3807d1f88e5c26de0345a8bfefc70f12f87efb8433", size = 9997, upload-time = "2025-06-14T20:48:29.833Z" },
@@ -1589,7 +1489,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/65/e9/d3231c4f87d07b8525401fd6ad3c56607c9e512c5490f0a7a6abb13acab6/pyobjc_framework_coretext-11.1.tar.gz", hash = "sha256:a29bbd5d85c77f46a8ee81d381b847244c88a3a5a96ac22f509027ceceaffaf6", size = 274702, upload-time = "2025-06-14T20:57:16.059Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/59/0c/0117d5353b1d18f8f8dd1e0f48374e4819cfcf3e8c34c676353e87320e8f/pyobjc_framework_coretext-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:515be6beb48c084ee413c00c4e9fbd6e730c1b8a24270f4c618fc6c7ba0011ce", size = 30072, upload-time = "2025-06-14T20:48:33.341Z" },
{ url = "https://files.pythonhosted.org/packages/4c/59/d6cc5470157cfd328b2d1ee2c1b6f846a5205307fce17291b57236d9f46e/pyobjc_framework_coretext-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b4f4d2d2a6331fa64465247358d7aafce98e4fb654b99301a490627a073d021e", size = 30072, upload-time = "2025-06-14T20:48:34.248Z" },
{ url = "https://files.pythonhosted.org/packages/32/67/9cc5189c366e67dc3e5b5976fac73cc6405841095f795d3fa0d5fc43d76a/pyobjc_framework_coretext-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1597bf7234270ee1b9963bf112e9061050d5fb8e1384b3f50c11bde2fe2b1570", size = 30175, upload-time = "2025-06-14T20:48:35.023Z" },
{ url = "https://files.pythonhosted.org/packages/b0/d1/6ec2ef4f8133177203a742d5db4db90bbb3ae100aec8d17f667208da84c9/pyobjc_framework_coretext-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:37e051e8f12a0f47a81b8efc8c902156eb5bc3d8123c43e5bd4cebd24c222228", size = 30180, upload-time = "2025-06-14T20:48:35.766Z" },
@@ -1608,7 +1507,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/c6/d8/03aff3c75485fc999e260946ef1e9adf17640a6e08d7bf603d31cfcf73fc/pyobjc_framework_corewlan-11.1.tar.gz", hash = "sha256:4a8afea75393cc0a6fe696e136233aa0ed54266f35a47b55a3583f4cb078e6ce", size = 65792, upload-time = "2025-06-14T20:57:16.931Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/35/41/d093fcb0b8bb5eb9d78fff3a84237eaa90a6ee24fd95dcc61be31069969b/pyobjc_framework_corewlan-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8a30698aea3a2c5130f4ff309bda45029f66ef76574d3cefce6159e9a5cc6bdd", size = 9985, upload-time = "2025-06-14T20:48:40.238Z" },
{ url = "https://files.pythonhosted.org/packages/03/ba/e73152fc1beee1bf75489d4a6f89ebd9783340e50ca1948cde029d7b0411/pyobjc_framework_corewlan-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e12f127b37a7ab8f349167332633392f2d6d29b87c9b98137a289d0fc1e07b5b", size = 9993, upload-time = "2025-06-14T20:48:41.081Z" },
{ url = "https://files.pythonhosted.org/packages/09/8a/74feabaad1225eb2c44d043924ed8caea31683e6760cd9b918b8d965efea/pyobjc_framework_corewlan-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7bd0775d2466ad500aad4747d8a889993db3a14240239f30ef53c087745e9c8e", size = 10016, upload-time = "2025-06-14T20:48:41.792Z" },
{ url = "https://files.pythonhosted.org/packages/ef/12/792146e163aa4504bc7870c77c4ec2425f9a05fa615a2b5c9cbec89b0fc6/pyobjc_framework_corewlan-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3c66643a97fcf3aa797fda997a3afc28d8d9bba9727dd5c0e68a313899d780f7", size = 10026, upload-time = "2025-06-14T20:48:42.626Z" },
@@ -1627,7 +1525,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/eb/92/7fab6fcc6bb659d6946cfb2f670058180bcc4ca1626878b0f7c95107abf0/pyobjc_framework_cryptotokenkit-11.1.tar.gz", hash = "sha256:5f82f44d9ab466c715a7c8ad4d5ec47c68aacd78bd67b5466a7b8215a2265328", size = 59223, upload-time = "2025-06-14T20:57:17.658Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6f/f3/ac15493b74351b27edbf13a84b5290ef1bbaf58ec627636946577eb75154/pyobjc_framework_cryptotokenkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d53ef13571afab5b2df5b2c118c3f296abae095abe6f0c9ebd105bab31527369", size = 12517, upload-time = "2025-06-14T20:48:46.489Z" },
{ url = "https://files.pythonhosted.org/packages/eb/b6/783495dc440277a330930bac7b560cf54d5e1838fc30fdc3162722db8a62/pyobjc_framework_cryptotokenkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b76fb928bc398091141dc52b26e02511065afd0b6de5533fa0e71ab13c51589", size = 12515, upload-time = "2025-06-14T20:48:47.346Z" },
{ url = "https://files.pythonhosted.org/packages/76/f1/4cb9c90a55ec13301d60ac1c4d774c37b4ebc6db6331d3853021c933fcc8/pyobjc_framework_cryptotokenkit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6384cb1d86fc586e2da934a5a37900825bd789e3a5df97517691de9af354af0c", size = 12543, upload-time = "2025-06-14T20:48:48.079Z" },
{ url = "https://files.pythonhosted.org/packages/c6/c8/b64a56ed65719b1dfb9c06da0772d4a76eceb830672aab237df745bc31f7/pyobjc_framework_cryptotokenkit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a55c0e57ab164aa5ce562e4d9e69026339067ecb4888638995690f1c43b79cfa", size = 12559, upload-time = "2025-06-14T20:48:49.115Z" },
@@ -1698,7 +1595,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/a5/b2/d8d1a28643c2ab681b517647bacb68496c98886336ffbd274f0b2ad28cdc/pyobjc_framework_discrecording-11.1.tar.gz", hash = "sha256:37585458e363b20bb28acdb5cc265dfca934d8a07b7baed2584953c11c927a87", size = 123004, upload-time = "2025-06-14T20:57:22.01Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/68/c2/6350befb08022c2aeb4729b710dd92f4e055a3f08c5c266adfd2cf0e270d/pyobjc_framework_discrecording-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9bae2419669ec3aadd3e7bf98dd92c80839242c7af4ab94364f5008cfe8e5603", size = 14560, upload-time = "2025-06-14T20:48:57.622Z" },
{ url = "https://files.pythonhosted.org/packages/a8/8c/0ff85cc34218e54236eb866e71c35e3308a661f50aea090d400e9121d9c4/pyobjc_framework_discrecording-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:dc8a7820fc193c2bfcd843c31de945dc45e77e5413089eabbc72be16a4f52e53", size = 14558, upload-time = "2025-06-14T20:48:58.495Z" },
{ url = "https://files.pythonhosted.org/packages/5e/17/032fa44bb66b6a20c432f3311072f88478b42dcf39b21ebb6c3bbdf2954f/pyobjc_framework_discrecording-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e29bc8c3741ae52fae092f892de856dbab2363e71537a8ae6fd026ecb88e2252", size = 14581, upload-time = "2025-06-14T20:48:59.228Z" },
{ url = "https://files.pythonhosted.org/packages/55/d4/a9e2fa7aa38b4ecca9668b3ae9ae4244bf335974c42b46313c3ec631c73a/pyobjc_framework_discrecording-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2d18158366d124852ad58291954611ebdcc43263a3bb75d7fd273408e67720e2", size = 14592, upload-time = "2025-06-14T20:49:00.002Z" },
@@ -1796,7 +1692,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/ce/7d/89adf16c7de4246477714dce8fcffae4242778aecd0c5f0ad9904725f42c/pyobjc_framework_extensionkit-11.1.tar.gz", hash = "sha256:c114a96f13f586dbbab8b6219a92fa4829896a645c8cd15652a6215bc8ff5409", size = 19766, upload-time = "2025-06-14T20:57:27.106Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6b/83/b1d6ea45dbb74e5cf3006d9bbbb32341c506b95b5827b3a8789d1c1bb9ad/pyobjc_framework_extensionkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:eb766b18ba23f15eeb1235c2a42f487591ff905644f9f12e44efe987ce3fbd38", size = 7894, upload-time = "2025-06-14T20:49:09.493Z" },
{ url = "https://files.pythonhosted.org/packages/0f/90/e6607b779756e039c0a4725a37cf70dc5b13c54a8cedbcf01ec1608866b1/pyobjc_framework_extensionkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61fd9f9758f95bcff2bf26fe475f679dfff9457d7130f114089e88fd5009675a", size = 7894, upload-time = "2025-06-14T20:49:10.593Z" },
{ url = "https://files.pythonhosted.org/packages/90/2a/93105b5452d2ff680a47e38a3ec6f2a37164babd95e0ab976c07984366de/pyobjc_framework_extensionkit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d505a64617c9db4373eb386664d62a82ba9ffc909bffad42cb4da8ca8e244c66", size = 7914, upload-time = "2025-06-14T20:49:11.842Z" },
{ url = "https://files.pythonhosted.org/packages/b8/67/1dbd000d9d0c17d838c471dbb48229fca1ca18fad8453c19ecc01d3312a1/pyobjc_framework_extensionkit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:abbadbea5b18e4a6944c3c428753ee298a133cbf601c70e9586b14e3aebf649b", size = 7927, upload-time = "2025-06-14T20:49:12.542Z" },
@@ -1815,7 +1710,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/d9/a3/519242e6822e1ddc9e64e21f717529079dbc28a353474420da8315d0a8b1/pyobjc_framework_externalaccessory-11.1.tar.gz", hash = "sha256:50887e948b78a1d94646422c243ac2a9e40761675e38b9184487870a31e83371", size = 23123, upload-time = "2025-06-14T20:57:27.845Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/84/e9/6291d37241113aa123c483ead96ef318b7cfe8333ce5b4cabaaa6afdf14d/pyobjc_framework_externalaccessory-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a36e2718d364373b10ac7b8151cffe8e3dedfcc72470fe2b6eed4e9c5d954034", size = 8887, upload-time = "2025-06-14T20:49:16.546Z" },
{ url = "https://files.pythonhosted.org/packages/63/54/d532badd43eba2db3fed2501b8e47a57cab233de2090ee97f4cff723e706/pyobjc_framework_externalaccessory-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a2b22f72b83721d841e5a3128df29fc41d785597357c6bbce84555a2b51a1e9d", size = 8887, upload-time = "2025-06-14T20:49:17.703Z" },
{ url = "https://files.pythonhosted.org/packages/7d/1b/e2def12aca9162b0fe0bbf0790d35595d46b2ef12603749c42af9234ffca/pyobjc_framework_externalaccessory-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:00caf75b959db5d14118d78c04085e2148255498839cdee735a0b9f6ef86b6a2", size = 8903, upload-time = "2025-06-14T20:49:18.393Z" },
{ url = "https://files.pythonhosted.org/packages/b4/6f/1340c193c30ade7b0394b2c8f29f3e6dd501eb23a416a728cc9a23efaec2/pyobjc_framework_externalaccessory-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:50b796a4721db87863a28cd55668cb1547fcc28834afda2032e500cdab5b3d95", size = 8915, upload-time = "2025-06-14T20:49:19.076Z" },
@@ -1834,7 +1728,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/1b/80/3ebba2c1e5e3aeae989fe038c259a93e7e7e18fd56666ece514d000d38ea/pyobjc_framework_fileprovider-11.1.tar.gz", hash = "sha256:748ca1c75f84afdf5419346a24bf8eec44dca071986f31f00071dc191b3e9ca8", size = 91696, upload-time = "2025-06-14T20:57:28.546Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/dd/25/d92b07c74b5638b1def3a5f702f2a75f8f901abce3dbe4d82d9e8c5a3155/pyobjc_framework_fileprovider-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:17e0da2e00900a1b25aca1cdbbda2c8097573ce07d6650d572968dff45c06ca7", size = 19547, upload-time = "2025-06-14T20:49:22.552Z" },
{ url = "https://files.pythonhosted.org/packages/d1/e4/c7b985d1199e3697ab5c3247027fe488b9d81b1fb597c34350942dc5838c/pyobjc_framework_fileprovider-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:888d6fb3fd625889ce0e409320c3379330473a386095cb4eda2b4caf0198ff66", size = 19546, upload-time = "2025-06-14T20:49:23.436Z" },
{ url = "https://files.pythonhosted.org/packages/49/b2/859d733b0110e56511478ba837fd8a7ba43aa8f8c7e5231b9e3f0258bfbf/pyobjc_framework_fileprovider-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ce6092dfe74c78c0b2abc03bfc18a0f5d8ddc624fc6a1d8dfef26d7796653072", size = 19622, upload-time = "2025-06-14T20:49:24.162Z" },
{ url = "https://files.pythonhosted.org/packages/91/ed/ae5ce4a18752ea2da5d7238f7847119af8c7dc69ffd9fb1369414c9745d2/pyobjc_framework_fileprovider-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9af41255df395a40a6e0b08c4410be5463f3ea91d8c9be61f6bd114252490ab2", size = 19627, upload-time = "2025-06-14T20:49:24.926Z" },
@@ -1879,7 +1772,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/8e/83/ec0b9ba355dbc34f27ed748df9df4eb6dbfdd9bbd614b0f193752f36f419/pyobjc_framework_fsevents-11.1.tar.gz", hash = "sha256:d29157d04124503c4dfa9dcbbdc8c34d3bab134d3db3a48d96d93f26bd94c14d", size = 29587, upload-time = "2025-06-14T20:57:30.796Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ae/ea/23a9ad2f1c6a93f6da34a7300b59ca213fa96eb846d226278a509953ed0f/pyobjc_framework_fsevents-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0f51d55e94fd84bc585a5c4ee63634297e192b256298a1372405649054220d13", size = 13085, upload-time = "2025-06-14T20:49:31.368Z" },
{ url = "https://files.pythonhosted.org/packages/14/6a/25118832a128db99a53be4c45f473192f72923d9b9690785539cee1a9858/pyobjc_framework_fsevents-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:95cc5d839d298b8e95175fb72df8a8e1b08773fd2e0d031efe91eee23e0c8830", size = 13076, upload-time = "2025-06-14T20:49:32.269Z" },
{ url = "https://files.pythonhosted.org/packages/13/c7/378d78e0fd956370f2b120b209117384b5b98925c6d8210a33fd73db4a15/pyobjc_framework_fsevents-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8b51d120b8f12a1ca94e28cf74113bf2bfd4c5aee7035b452e895518f4df7630", size = 13147, upload-time = "2025-06-14T20:49:33.022Z" },
{ url = "https://files.pythonhosted.org/packages/18/dc/3b7e75b9f8284257740679509b54f61da2a114cf805d7d3523053e4c6c19/pyobjc_framework_fsevents-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fad5ada269f137afabd622b5fc04884c668ae1c7914a8791bab73b1d972f7713", size = 13164, upload-time = "2025-06-14T20:49:33.751Z" },
@@ -1898,7 +1790,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/46/47/d1f04c6115fa78936399a389cc5e0e443f8341c9a6c1c0df7f6fdbe51286/pyobjc_framework_fskit-11.1.tar.gz", hash = "sha256:9ded1eab19b4183cb04381e554bbbe679c1213fd58599d6fc6e135e93b51136f", size = 42091, upload-time = "2025-06-14T20:57:31.504Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/02/a1/2b32e3deac3b12a4a26862514aa1827c9e26bacf0a6226cedbfbcbbbcafb/pyobjc_framework_fskit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:db96e20789186b5f3be132cc7041e38cdaf98904da82b80fbcb2564365738517", size = 19918, upload-time = "2025-06-14T20:49:37.399Z" },
{ url = "https://files.pythonhosted.org/packages/16/76/1152bd8121ef2c9a0ccdf10624d647095ce944d34f654f001b458edef668/pyobjc_framework_fskit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59a939ac8442d648f73a3da75923aa3637ac4693850d995f1914260c8f4f7947", size = 19922, upload-time = "2025-06-14T20:49:38.424Z" },
{ url = "https://files.pythonhosted.org/packages/59/8f/db8f03688db77bfa4b78e89af1d89e910c5e877e94d58bdb3e93cc302e5d/pyobjc_framework_fskit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1e50b8f949f1386fada73b408463c87eb81ef7fd0b3482bacf0c206a73723013", size = 19948, upload-time = "2025-06-14T20:49:39.18Z" },
{ url = "https://files.pythonhosted.org/packages/7a/31/0dd6ad9dfce080d6e567326fe7243261740ef1090f72409322040f55a426/pyobjc_framework_fskit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cc2390934a23b6407aa7802b11978374301444c3135835ad3373f7b4930c24eb", size = 19959, upload-time = "2025-06-14T20:49:39.941Z" },
@@ -1917,7 +1808,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/1b/8e/b594fd1dc32a59462fc68ad502be2bd87c70e6359b4e879a99bcc4beaf5b/pyobjc_framework_gamecenter-11.1.tar.gz", hash = "sha256:a1c4ed54e11a6e4efba6f2a21ace92bcf186e3fe5c74a385b31f6b1a515ec20c", size = 31981, upload-time = "2025-06-14T20:57:32.192Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3c/f9/65263342922e54dde4255832b9dfb17ae03454e9e07da209db30edc55313/pyobjc_framework_gamecenter-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8543725d4fad635bbbe3aaeea0df8d31a419f2cab0d9f9b411ae2212c8fac5eb", size = 18608, upload-time = "2025-06-14T20:49:44.072Z" },
{ url = "https://files.pythonhosted.org/packages/21/a8/8d9c2d0ff9f42a0951063a9eaff1e39c46c15e89ce4e5e274114340ca976/pyobjc_framework_gamecenter-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:81abe136292ea157acb6c54871915fe6d386146a9386179ded0b974ac435045c", size = 18601, upload-time = "2025-06-14T20:49:44.946Z" },
{ url = "https://files.pythonhosted.org/packages/99/52/0e56f21a6660a4f43882ec641b9e19b7ea92dc7474cec48cda1c9bed9c49/pyobjc_framework_gamecenter-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:779cdf8f52348be7f64d16e3ea37fd621d5ee933c032db3a22a8ccad46d69c59", size = 18634, upload-time = "2025-06-14T20:49:45.737Z" },
{ url = "https://files.pythonhosted.org/packages/3e/fc/64a1e9dc4874a75ceed6e70bb07d5e2a3460283c7737e639a0408ec1b365/pyobjc_framework_gamecenter-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ff8905a5a7bfd86cb2b95671b452be0836f79db065b8d8b3bb2a1a5750ffd0d", size = 18638, upload-time = "2025-06-14T20:49:46.826Z" },
@@ -1936,7 +1826,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/70/4c/1dd62103092a182f2ab8904c8a8e3922d2b0a80a7adab0c20e5fd0207d75/pyobjc_framework_gamecontroller-11.1.tar.gz", hash = "sha256:4d5346faf90e1ebe5602c0c480afbf528a35a7a1ad05f9b49991fdd2a97f105b", size = 115783, upload-time = "2025-06-14T20:57:32.879Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/87/dc/3371ae73f99f25ee62db9ee6259de20687dbed2a7f3679b39b86ce9347e4/pyobjc_framework_gamecontroller-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f19e4e645966e99c08552d0841c9e535326506dfc0c0ef097a6ad62f71b7e99d", size = 20830, upload-time = "2025-06-14T20:49:50.66Z" },
{ url = "https://files.pythonhosted.org/packages/e5/8e/09e73e03e9f57e77df58cf77f6069d3455a3c388a890ff815e86d036ae39/pyobjc_framework_gamecontroller-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:782779f080508acf869187c0cbd3a48c55ee059d3a14fe89ccd6349537923214", size = 20825, upload-time = "2025-06-14T20:49:51.565Z" },
{ url = "https://files.pythonhosted.org/packages/40/e3/e35bccb0284046ef716db4897b70d061b8b16c91fb2c434b1e782322ef56/pyobjc_framework_gamecontroller-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d2cbc0c6c7d9c63e6b5b0b124d0c2bad01bb4b136f3cbc305f27d31f8aab6083", size = 20850, upload-time = "2025-06-14T20:49:52.401Z" },
{ url = "https://files.pythonhosted.org/packages/ae/eb/42469724725f5d0f11c197aadbb0c5db1647ba69579df4e8d13f553bed1c/pyobjc_framework_gamecontroller-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:4866b25df05f583af06095e7103ddd2fbb2484b0ac2c78fd2cd825f995e524fa", size = 20862, upload-time = "2025-06-14T20:49:53.47Z" },
@@ -1956,7 +1845,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/5b/7b/ba141ec0f85ca816f493d1f6fe68c72d01092e5562e53c470a0111d9c34b/pyobjc_framework_gamekit-11.1.tar.gz", hash = "sha256:9b8db075da8866c4ef039a165af227bc29393dc11a617a40671bf6b3975ae269", size = 165397, upload-time = "2025-06-14T20:57:33.711Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a4/50/6b67f3af13ed5c76e22b07ff78eccb1da4c336e59185ffd7506e8e8289bc/pyobjc_framework_gamekit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:18ce0e373613a0b9f78969218b884c3191958e353e3462fbfc6d51d758ada41c", size = 21934, upload-time = "2025-06-14T20:49:57.279Z" },
{ url = "https://files.pythonhosted.org/packages/b4/2a/f206682b9ff76983bae14a479a9c8a9098e58efc3db31f88211d6ad4fd42/pyobjc_framework_gamekit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5e07c25eab051905c6bd46f368d8b341ef8603dce588ff6dbd82d609dd4fbf71", size = 21932, upload-time = "2025-06-14T20:49:58.154Z" },
{ url = "https://files.pythonhosted.org/packages/1f/23/094e4fe38f2de029365604f0b7dffde7b0edfc57c3d388294c20ed663de2/pyobjc_framework_gamekit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f945c7cfe53c4a349a03a1272f2736cc5cf88fe9e7a7a407abb03899635d860c", size = 21952, upload-time = "2025-06-14T20:49:58.933Z" },
{ url = "https://files.pythonhosted.org/packages/22/2c/9a35fb83a1df7588e2e60488aa425058ee7f01b5a9d4947f74f62a130bf3/pyobjc_framework_gamekit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c7f2bf7ecf44ca678cfdf76f23b32d9c2d03006a0af9ad8e60d9114d6be640a", size = 21968, upload-time = "2025-06-14T20:49:59.688Z" },
@@ -1976,7 +1864,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/e0/07/f38b1d83eac10ea4f75c605ffc4850585740db89b90842d311e586ee36cd/pyobjc_framework_gameplaykit-11.1.tar.gz", hash = "sha256:9ae2bee69b0cc1afa0e210b4663c7cdbb3cc94be1374808df06f98f992e83639", size = 73399, upload-time = "2025-06-14T20:57:34.538Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ac/aa/e041c985c69a8de4af1ff74536225fed49880fd3e91568a7b235396b0473/pyobjc_framework_gameplaykit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8cc9b2a476f79d593d9617fdb8c5ac27d1cf9256063379e3df9b6519c462eb48", size = 13067, upload-time = "2025-06-14T20:50:04.353Z" },
{ url = "https://files.pythonhosted.org/packages/0f/29/df66f53f887990878b2b00b1336e451a15e360a384be74559acf47854bc3/pyobjc_framework_gameplaykit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ac9f50941988c30175149af481a49b2026c56a9a497c6dbf2974ffb50ffe0af8", size = 13065, upload-time = "2025-06-14T20:50:05.243Z" },
{ url = "https://files.pythonhosted.org/packages/e7/f5/65bdbefb9de7cbc2edf0b1f76286736536e31c216cfac1a5f84ea15f0fc1/pyobjc_framework_gameplaykit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0e4f34db8177b8b4d89fd22a2a882a6c9f6e50cb438ea2fbbf96845481bcd80d", size = 13091, upload-time = "2025-06-14T20:50:05.962Z" },
{ url = "https://files.pythonhosted.org/packages/25/4c/011e20a8e9ff1270d3efb6c470c3cd8af10dcd2b05042721b1a777aca7a6/pyobjc_framework_gameplaykit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:78c513bc53bafd996d896f6f4535f2700b4916013417f8b41f47045790c6208d", size = 13109, upload-time = "2025-06-14T20:50:06.7Z" },
@@ -1995,7 +1882,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/af/66/fa76f7c8e36e4c10677d42d91a8e220c135c610a06b759571db1abe26a32/pyobjc_framework_healthkit-11.1.tar.gz", hash = "sha256:20f59bd9e1ffafe5893b4eff5867fdfd20bd46c3d03bc4009219d82fc6815f76", size = 202009, upload-time = "2025-06-14T20:57:35.285Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/64/ec/7412ee0dd2e257a29765e0b18b8a683a5b199ed1494e480908b31ae8a187/pyobjc_framework_healthkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f15f2cff20a09f42f251752f908a54c5fe3adabb03ec8d3fb2b66ff7b0b4709e", size = 20301, upload-time = "2025-06-14T20:50:11.302Z" },
{ url = "https://files.pythonhosted.org/packages/70/aa/c337d27dd98ffcbba2b1200126fcf624d1ccbeb7a4ed9205d48bfe2c1ca8/pyobjc_framework_healthkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:34bce3d144c461af7e577fcf6bbb7739d0537bf42f081960122923a7ef2e06c0", size = 20301, upload-time = "2025-06-14T20:50:12.158Z" },
{ url = "https://files.pythonhosted.org/packages/c7/08/12fca070ad2dc0b9c311df209b9b6d275ee192cb5ccbc94616d9ddd80d88/pyobjc_framework_healthkit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab4350f9fe65909107dd7992b367a6c8aac7dc31ed3d5b52eeb2310367d0eb0b", size = 20311, upload-time = "2025-06-14T20:50:13.271Z" },
{ url = "https://files.pythonhosted.org/packages/5d/26/0337f1b4607a3a13a671a6b07468726943e0d28a462998fcd902f7df6fbf/pyobjc_framework_healthkit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8b6c739e17362897f0b1ba4aa4dc395b3d0c3855b87423eaeb6a89f910adc43f", size = 20330, upload-time = "2025-06-14T20:50:14.042Z" },
@@ -2014,7 +1900,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/7b/3b/f4edbc58a8c7394393f8d00d0e764f655545e743ee4e33917f27b8c68e7b/pyobjc_framework_imagecapturecore-11.1.tar.gz", hash = "sha256:a610ceb6726e385b132a1481a68ce85ccf56f94667b6d6e1c45a2cfab806a624", size = 100398, upload-time = "2025-06-14T20:57:36.503Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/71/fd/4dd8ff8daaec5ba80e57404136b553f99cd8cdbb5a965f6e4d4f13c08070/pyobjc_framework_imagecapturecore-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:69f91c9f17bf0b8332b5826033bc5292493fe575fdb841cd7f58ab493053de38", size = 16002, upload-time = "2025-06-14T20:50:17.795Z" },
{ url = "https://files.pythonhosted.org/packages/50/72/465741d33757ef2162a1c9e12d6c8a41b5490949a92431c42a139c132303/pyobjc_framework_imagecapturecore-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ede4c15da909a4d819c732a5554b8282a7b56a1b73d82aef908124147921945a", size = 15999, upload-time = "2025-06-14T20:50:18.742Z" },
{ url = "https://files.pythonhosted.org/packages/61/62/54ed61e7cd3213549c8e98ca87a6b21afbb428d2c41948ae48ea019bf973/pyobjc_framework_imagecapturecore-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed296c23d3d8d1d9af96a6486d09fb8d294cc318e4a2152e6f134151c76065f8", size = 16021, upload-time = "2025-06-14T20:50:19.836Z" },
{ url = "https://files.pythonhosted.org/packages/4e/91/71d48ec1b29d57112edd33ada86fcdbf1c9423ef2bdddadf8d37e8a03492/pyobjc_framework_imagecapturecore-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ded8dc6a8c826a6ae1b6a6d0a31542bd1eb85345f86201689c54e51193b572dc", size = 16030, upload-time = "2025-06-14T20:50:20.568Z" },
@@ -2033,7 +1918,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/02/32/6a90bba682a31960ba1fc2d3b263e9be26043c4fb7aed273c13647c8b7d9/pyobjc_framework_inputmethodkit-11.1.tar.gz", hash = "sha256:7037579524041dcee71a649293c2660f9359800455a15e6a2f74a17b46d78496", size = 27203, upload-time = "2025-06-14T20:57:37.246Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5e/59/94750df04415dce0571673aa9cfed50feb2115df33b6b4ace8d0c57e7679/pyobjc_framework_inputmethodkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ccf8697a13e7ab5e3ec446b930f40069da43823bfc678c4c426ad03f980c14f", size = 9479, upload-time = "2025-06-14T20:50:24.345Z" },
{ url = "https://files.pythonhosted.org/packages/7f/23/a4226040eec8ed930c81073776064f30d627db03e9db5b24720aad8fd14d/pyobjc_framework_inputmethodkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9b0e47c3bc7f1e628c906436c1735041ed2e9aa7cba3f70084b6311c63c508be", size = 9480, upload-time = "2025-06-14T20:50:25.184Z" },
{ url = "https://files.pythonhosted.org/packages/a8/0d/8a570072096fe339702e4ae9d98e59ee7c6c14124d4437c9a8c4482dda6d/pyobjc_framework_inputmethodkit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dd0c591a9d26967018a781fa4638470147ef2a9af3ab4a28612f147573eeefba", size = 9489, upload-time = "2025-06-14T20:50:25.875Z" },
{ url = "https://files.pythonhosted.org/packages/dc/a5/ce000bba1a52287c21d1d3aff6779a6bbb463da4337573cb17ecc9475939/pyobjc_framework_inputmethodkit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5095005809a4108f362998b46994f99b5a57f9ba367c01141c1b9eaea311bc5b", size = 9508, upload-time = "2025-06-14T20:50:26.577Z" },
@@ -2079,7 +1963,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/4c/af/d7f260d06b79acca8028e373c2fe30bf0be014388ba612f538f40597d929/pyobjc_framework_intents-11.1.tar.gz", hash = "sha256:13185f206493f45d6bd2d4903c2136b1c4f8b9aa37628309ace6ff4a906b4695", size = 448459, upload-time = "2025-06-14T20:57:39.589Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4d/cb/123127c6fa04ddabe9e92db234cbf38106b2e4d5e301e5cb26dd45070d28/pyobjc_framework_intents-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:315f8572336dee42ab582435e85176a14455928ac451fcb1f7c62786d17e8758", size = 32230, upload-time = "2025-06-14T20:50:33.03Z" },
{ url = "https://files.pythonhosted.org/packages/c5/1d/10fdbf3b8dd6451465ae147143ba3159397a50ff81aed1eb86c153e987b5/pyobjc_framework_intents-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da2f11ee64c75cfbebb1c2be52a20b3618f32b6c47863809ff64c61e8a1dffb9", size = 32227, upload-time = "2025-06-14T20:50:34.303Z" },
{ url = "https://files.pythonhosted.org/packages/8a/37/e6fa5737da42fb1265041bd3bd4f2be96f09294018fabf07139dd9dbc7b9/pyobjc_framework_intents-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a663e2de1b7ae7b547de013f89773963f8180023e36f2cebfe8060395dc34c33", size = 32253, upload-time = "2025-06-14T20:50:35.028Z" },
{ url = "https://files.pythonhosted.org/packages/f0/ff/f793a0c4b5ea87af3fc228d74e457c1594695b2745b3007a8ef4832ebeb7/pyobjc_framework_intents-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9e21b3bc33de2d5f69b5c1d581e5c724a08686fe84ec324a4be365bef769e482", size = 32266, upload-time = "2025-06-14T20:50:35.775Z" },
@@ -2098,7 +1981,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/86/46/20aae4a71efb514b096f36273a6129b48b01535bf501e5719d4a97fcb3a5/pyobjc_framework_intentsui-11.1.tar.gz", hash = "sha256:c8182155af4dce369c18d6e6ed9c25bbd8110c161ed5f1b4fb77cf5cdb99d135", size = 21305, upload-time = "2025-06-14T20:57:40.477Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/96/e1/d7099a851a165e1b344d36b8179658f6ec31ed9ce98c7057d07b66b3f339/pyobjc_framework_intentsui-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:381c14d60170f71e89b5fd4eae84c0821c50a70b08ce9994286177fa37b8d79e", size = 8938, upload-time = "2025-06-14T20:50:39.635Z" },
{ url = "https://files.pythonhosted.org/packages/31/e3/db74fc161bb85bc442dfddf50321924613b67cf49288e2a8b335bf6d546a/pyobjc_framework_intentsui-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:252f7833fabb036cd56d59b445922b25cda1561b54c0989702618a5561d8e748", size = 8936, upload-time = "2025-06-14T20:50:40.522Z" },
{ url = "https://files.pythonhosted.org/packages/43/7c/77fbd2a6f85eb905fbf27ba7540eaf2a026771ed5100fb1c01143cf47e9b/pyobjc_framework_intentsui-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:99a3ae40eb2a6ef1125955dd513c8acc88ce7d8d90130a8cdeaec8336e6fbec5", size = 8965, upload-time = "2025-06-14T20:50:41.281Z" },
{ url = "https://files.pythonhosted.org/packages/9b/d6/ce8e2f6354bd77271b8f9f2a05920fb0a6de57ab5d97033021672853acb5/pyobjc_framework_intentsui-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:154fd92112184e8ef29ce81e685c377422dffcff4f7900ea6e5956a0e2be2268", size = 8983, upload-time = "2025-06-14T20:50:41.96Z" },
@@ -2117,7 +1999,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/93/e0/74b7b10c567b66c5f38b45ab240336325a4c889f43072d90f2b90aaeb7c0/pyobjc_framework_iobluetooth-11.1.tar.gz", hash = "sha256:094fd4be60cd1371b17cb4b33a3894e0d88a11b36683912be0540a7d51de76f1", size = 300992, upload-time = "2025-06-14T20:57:41.256Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/5c/bbb6fd9b3ef8f7d7f2674f193cf96ec6e8955ae677c26812eef5f5da08ea/pyobjc_framework_iobluetooth-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d512252b8ee2a23c88d5e0a188f8949858f1ef3b99c279fb412f3e00508ec367", size = 40380, upload-time = "2025-06-14T20:50:45.428Z" },
{ url = "https://files.pythonhosted.org/packages/0a/13/31a514e48bd54880aadb1aac3a042fca5f499780628c18f4f54f06d4ece2/pyobjc_framework_iobluetooth-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7d8858cf2e4b2ef5e8bf29b76c06d4f2e6a2264c325146d07dfab94c46633329", size = 40378, upload-time = "2025-06-14T20:50:46.298Z" },
{ url = "https://files.pythonhosted.org/packages/da/94/eef57045762e955795a4e3312674045c52f8c506133acf9efe1b3370b93f/pyobjc_framework_iobluetooth-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:883781e7223cb0c63fab029d640721ded747f2e2b067645bc8b695ef02a4a4dd", size = 40406, upload-time = "2025-06-14T20:50:47.101Z" },
{ url = "https://files.pythonhosted.org/packages/ed/f5/24476d6919c2d8d849c88740e81f620663181b3c97ac6e3aaeb1833277a5/pyobjc_framework_iobluetooth-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:4a8b1caba9ac51435f64a6cf9c1a2be867603161af8bebdd1676072ebed2fed9", size = 40428, upload-time = "2025-06-14T20:50:47.85Z" },
@@ -2214,7 +2095,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/be/89/7830c293ba71feb086cb1551455757f26a7e2abd12f360d375aae32a4d7d/pyobjc_framework_libdispatch-11.1.tar.gz", hash = "sha256:11a704e50a0b7dbfb01552b7d686473ffa63b5254100fdb271a1fe368dd08e87", size = 53942, upload-time = "2025-06-14T20:57:45.903Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7f/33/7a6b509e85d95ed5aa7c813c6bccfe4e0a1162baa02f51050d1da91408a9/pyobjc_framework_libdispatch-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9c598c073a541b5956b5457b94bd33b9ce19ef8d867235439a0fad22d6beab49", size = 20444, upload-time = "2025-06-14T20:50:57.316Z" },
{ url = "https://files.pythonhosted.org/packages/b0/cd/1010dee9f932a9686c27ce2e45e91d5b6875f5f18d2daafadea70090e111/pyobjc_framework_libdispatch-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ddca472c2cbc6bb192e05b8b501d528ce49333abe7ef0eef28df3133a8e18b7", size = 20441, upload-time = "2025-06-14T20:50:58.3Z" },
{ url = "https://files.pythonhosted.org/packages/ac/92/ff9ceb14e1604193dcdb50643f2578e1010c68556711cd1a00eb25489c2b/pyobjc_framework_libdispatch-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dc9a7b8c2e8a63789b7cf69563bb7247bde15353208ef1353fff0af61b281684", size = 15627, upload-time = "2025-06-14T20:50:59.055Z" },
{ url = "https://files.pythonhosted.org/packages/0f/10/5851b68cd85b475ff1da08e908693819fd9a4ff07c079da9b0b6dbdaca9c/pyobjc_framework_libdispatch-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c4e219849f5426745eb429f3aee58342a59f81e3144b37aa20e81dacc6177de1", size = 15648, upload-time = "2025-06-14T20:50:59.809Z" },
@@ -2233,7 +2113,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/6a/c9/7e15e38ac23f5bfb4e82bdf3b7ef88e2f56a8b4ad884009bc2d5267d2e1f/pyobjc_framework_libxpc-11.1.tar.gz", hash = "sha256:8fd7468aa520ff19915f6d793070b84be1498cb87224bee2bad1f01d8375273a", size = 49135, upload-time = "2025-06-14T20:57:46.59Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/36/e2/94c02aac855ab52a89408a90f16bdd69cf461be5de430336521f6588e34c/pyobjc_framework_libxpc-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:427ce45f700720198c365a099fb2f4f2fa28dbf85a7c4076371f61dbd16a0b6f", size = 19464, upload-time = "2025-06-14T20:51:04.038Z" },
{ url = "https://files.pythonhosted.org/packages/39/01/f5fbc7627f838aea5960f3287b75cbda9233f76fc3ba82f088630d5d16cc/pyobjc_framework_libxpc-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ec8a7df24d85a561fc21d0eb0db89e8cddefeedec71c69bccf17f99804068ed", size = 19466, upload-time = "2025-06-14T20:51:05.138Z" },
{ url = "https://files.pythonhosted.org/packages/be/8f/dfd8e1e1e461f857a1e50138e69b17c0e62a8dcaf7dea791cc158d2bf854/pyobjc_framework_libxpc-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c29b2df8d74ff6f489afa7c39f7c848c5f3d0531a6bbe704571782ee6c820084", size = 19573, upload-time = "2025-06-14T20:51:05.902Z" },
{ url = "https://files.pythonhosted.org/packages/00/fa/9ac86892294428a0eb532242a6fcbec565d0cf0e919924b6b7c064c8b196/pyobjc_framework_libxpc-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6862e63f565823d4eeb56f18f90a3ee8682c52a8d4bcd486d3535c9959464eda", size = 19578, upload-time = "2025-06-14T20:51:06.659Z" },
@@ -2267,7 +2146,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/e5/27/9e3195f3561574140e9b9071a36f7e0ebd18f50ade9261d23b5b9df8fccd/pyobjc_framework_localauthentication-11.1.tar.gz", hash = "sha256:3cd48907c794bd414ac68b8ac595d83c7e1453b63fc2cfc2d2035b690d31eaa1", size = 40700, upload-time = "2025-06-14T20:57:47.931Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3d/bc/8835da9e32d7bf341643344ca85d02d16520a52525f087c2d8c786bcc7c2/pyobjc_framework_localauthentication-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f433e611a910d89a1e327f87e2b3bd9bf33576fd8b964767487e6f278003b030", size = 10712, upload-time = "2025-06-14T20:51:11.631Z" },
{ url = "https://files.pythonhosted.org/packages/4e/9a/acc10d45041445db99a121950b0d4f4ff977dbe5e95ec154fe2e1740ff08/pyobjc_framework_localauthentication-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b6d52d07abd2240f7bc02b01ea1c630c280ed3fbc3fabe1e43b7444cfd41788", size = 10707, upload-time = "2025-06-14T20:51:12.436Z" },
{ url = "https://files.pythonhosted.org/packages/91/db/59f118cc2658814c6b501b7360ca4fe6a82fd289ced5897b99787130ceef/pyobjc_framework_localauthentication-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:aa3815f936612d78e51b53beed9115c57ae2fd49500bb52c4030a35856e6569e", size = 10730, upload-time = "2025-06-14T20:51:13.487Z" },
{ url = "https://files.pythonhosted.org/packages/9f/8b/544cadc6ecf75def347e96cdae4caa955bc23f2bc314779cffe1e6ba9475/pyobjc_framework_localauthentication-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9c9446c017b13c8dcadf485b76ab1d7bc12099b504bf5c2df1aae33b5dc4ab2c", size = 10748, upload-time = "2025-06-14T20:51:14.198Z" },
@@ -2315,7 +2193,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/57/f0/505e074f49c783f2e65ca82174fd2d4348568f3f7281c1b81af816cf83bb/pyobjc_framework_mapkit-11.1.tar.gz", hash = "sha256:f3a5016f266091be313a118a42c0ea4f951c399b5259d93639eb643dacc626f1", size = 165614, upload-time = "2025-06-14T20:57:50.362Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/72/1d/643b240dd2c95a4a35d5f1085570b71163ed9019eae540279b44b4812663/pyobjc_framework_mapkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0304816336b179a9508b6df9b7558c66e058acadf911900437db2d5b50eebecd", size = 22486, upload-time = "2025-06-14T20:51:19.762Z" },
{ url = "https://files.pythonhosted.org/packages/a0/dc/a7e03a9066e6eed9d1707ae45453a5332057950e16de6665402c804ae7af/pyobjc_framework_mapkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:daee6bedc3acc23e62d1e7c3ab97e10425ca57e0c3cc47d2b212254705cc5c44", size = 22481, upload-time = "2025-06-14T20:51:20.694Z" },
{ url = "https://files.pythonhosted.org/packages/30/0a/50aa2fba57499ff657cacb9ef1730006442e4f42d9a822dae46239603ecc/pyobjc_framework_mapkit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:91976c6dbc8cbb020e059a0ccdeab8933184712f77164dbad5a5526c1a49599d", size = 22515, upload-time = "2025-06-14T20:51:21.439Z" },
{ url = "https://files.pythonhosted.org/packages/78/54/792f4d5848176753bfde8f10ac21b663981adf940243765edad45908cd55/pyobjc_framework_mapkit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b6fa1c4fffc3ae91adb965731a0cc943b3b6e82c8f21919a53a68b43a67b534", size = 22534, upload-time = "2025-06-14T20:51:22.199Z" },
@@ -2349,7 +2226,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/e1/09/fd214dc0cf3f3bc3f528815af4799c0cb7b4bf4032703b19ea63486a132b/pyobjc_framework_mediaextension-11.1.tar.gz", hash = "sha256:85a1c8a94e9175fb364c453066ef99b95752343fd113f08a3805cad56e2fa709", size = 58489, upload-time = "2025-06-14T20:57:51.796Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/70/e6/6099004368b7051954cc4cceb856e3dceefe549f3fba890c6f9c464ee98a/pyobjc_framework_mediaextension-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7f8a41ae51c1c70ea273f29857adc24c1d7bafc8071f0e6b50cb12b8ec5c4eb2", size = 38858, upload-time = "2025-06-14T20:51:27.424Z" },
{ url = "https://files.pythonhosted.org/packages/ec/25/95315f730e9b73ef9e8936ed3ded636d3ac71b4d5653d4caf1d20a2314a8/pyobjc_framework_mediaextension-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:915c0cbb04913beb1f1ac8939dc0e615da8ddfba3927863a476af49f193415c5", size = 38858, upload-time = "2025-06-14T20:51:28.296Z" },
{ url = "https://files.pythonhosted.org/packages/56/78/2c2d8265851f6060dbf4434c21bd67bf569b8c3071ba1f257e43aae563a8/pyobjc_framework_mediaextension-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:06cb19004413a4b08dd75cf1e5dadea7f2df8d15feeeb7adb529d0cf947fa789", size = 38859, upload-time = "2025-06-14T20:51:29.102Z" },
{ url = "https://files.pythonhosted.org/packages/e7/6b/1d3761316ca7df57700a68b28f7c00cc4f050b3f6debac2305219506d6b1/pyobjc_framework_mediaextension-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:40f1440ccc8da6deb80810866f8c807c17567db67b53e1576ea3a3b1330c85f9", size = 38870, upload-time = "2025-06-14T20:51:29.862Z" },
@@ -2395,7 +2271,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/e1/68/cc230d2dfdeb974fdcfa828de655a43ce2bf4962023fd55bbb7ab0970100/pyobjc_framework_mediatoolbox-11.1.tar.gz", hash = "sha256:97834addc5179b3165c0d8cd74cc97ad43ed4c89547724216426348aca3b822a", size = 23568, upload-time = "2025-06-14T20:57:53.913Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c1/e4/6be29e396803553b1e8c400249a7010566c16e3320b13b246ce972fbd30a/pyobjc_framework_mediatoolbox-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c6beb3be7bb3e899b8e6e7c328c5d94e706b64f10023a49a108d74c03d132545", size = 12624, upload-time = "2025-06-14T20:51:35.986Z" },
{ url = "https://files.pythonhosted.org/packages/99/bc/6b69ca3c2bf1573b907be460c6a413ff2dfd1c037da53f46aec3bcdb3c73/pyobjc_framework_mediatoolbox-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da60c0409b18dfb9fa60a60589881e1382c007700b99722926270feadcf3bfc1", size = 12630, upload-time = "2025-06-14T20:51:36.873Z" },
{ url = "https://files.pythonhosted.org/packages/b5/23/6b5d999e1e71c42d5d116d992515955ac1bbc5cf4890072bb26f38eb9802/pyobjc_framework_mediatoolbox-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2867c91645a335ee29b47e9c0e9fd3ea8c9daad0c0719c50b8bf244d76998056", size = 12785, upload-time = "2025-06-14T20:51:37.593Z" },
{ url = "https://files.pythonhosted.org/packages/29/05/24d60869a816418771653057720727d6df2dd8485302a21f80cfcb694110/pyobjc_framework_mediatoolbox-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bf26348d20caef38efb9cfc02d28af83c930b2f2c9581407f8ec04b3d8321a7a", size = 12794, upload-time = "2025-06-14T20:51:38.278Z" },
@@ -2414,7 +2289,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/af/cf/29fea96fd49bf72946c5dac4c43ef50f26c15e9f76edd6f15580d556aa23/pyobjc_framework_metal-11.1.tar.gz", hash = "sha256:f9fd3b7574a824632ee9b7602973da30f172d2b575dd0c0f5ef76b44cfe9f6f9", size = 446549, upload-time = "2025-06-14T20:57:54.731Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/45/53/c785c8de4689393b65abd324b369cc31586d7599f62ac07db40f6936d85c/pyobjc_framework_metal-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9c77f71b7499a27f90d43a34ccd41de15c1ee8c33f9fb4293e1395d88c2aaae1", size = 58063, upload-time = "2025-06-14T20:51:43.32Z" },
{ url = "https://files.pythonhosted.org/packages/e9/e8/cd0621e246dc0dc06f55c50af3002573ad19208e30f6806ec997ac587886/pyobjc_framework_metal-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:157a0052be459ffb35a3687f77a96ea87b42caf4cdd0b9f7245242b100edb4f0", size = 58066, upload-time = "2025-06-14T20:51:44.243Z" },
{ url = "https://files.pythonhosted.org/packages/4c/94/3d5a8bed000dec4a13e72dde175898b488192716b7256a05cc253c77020d/pyobjc_framework_metal-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f3aae0f9a4192a7f4f158dbee126ab5ef63a81bf9165ec63bc50c353c8d0e6f", size = 57969, upload-time = "2025-06-14T20:51:45.051Z" },
{ url = "https://files.pythonhosted.org/packages/4f/af/b1f78770bb4b8d73d7a70140e39ca92daa2ba6b8de93d52b2ebf9db7d03e/pyobjc_framework_metal-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d9b24d0ddb98b34a9a19755e5ca507c62fcef40ee5eae017e39be29650137f8c", size = 57994, upload-time = "2025-06-14T20:51:46.209Z" },
@@ -2433,7 +2307,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/10/20/4c839a356b534c161fb97e06589f418fc78cc5a0808362bdecf4f9a61a8d/pyobjc_framework_metalfx-11.1.tar.gz", hash = "sha256:555c1b895d4ba31be43930f45e219a5d7bb0e531d148a78b6b75b677cc588fd8", size = 27002, upload-time = "2025-06-14T20:57:55.949Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/92/91/eb5dec63182d0a515c0a5eb9b08d908bb31b1eac9fc784fbd0240afd80a0/pyobjc_framework_metalfx-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fae511ea96f4ce8aff89ee71294c26294863b5a87b6665e9b4c1b47fd7ebe6ea", size = 10097, upload-time = "2025-06-14T20:51:50.292Z" },
{ url = "https://files.pythonhosted.org/packages/a2/f5/df29eeaaf053cd931fb74204a5f8827f88875a81c456b1e0fa24ea0bbcee/pyobjc_framework_metalfx-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cbfca74f437fcde89de85d14de33c2e617d3084f5fc2b4d614a700e516324f55", size = 10091, upload-time = "2025-06-14T20:51:51.084Z" },
{ url = "https://files.pythonhosted.org/packages/36/73/a8df8fa445a09fbc917a495a30b13fbcf224b5576c1e464d5ece9824a493/pyobjc_framework_metalfx-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:60e1dcdf133d2504d810c3a9ba5a02781c9d54c2112a9238de8e3ca4e8debf31", size = 10107, upload-time = "2025-06-14T20:51:51.783Z" },
{ url = "https://files.pythonhosted.org/packages/8e/7b/4d925bf5f1f0b0d254b3167999987ecafb251f589cd863bdbaf96eb4ad2a/pyobjc_framework_metalfx-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fdced91f6b2012c556db954de0e17f6d7985d52b4af83262f4d083bcd87aa01c", size = 10122, upload-time = "2025-06-14T20:51:52.473Z" },
@@ -2453,7 +2326,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/45/cb/7e01bc61625c7a6fea9c9888c9ed35aa6bbc47cda2fcd02b6525757bc2b8/pyobjc_framework_metalkit-11.1.tar.gz", hash = "sha256:8811cd81ee9583b9330df4f2499a73dcc53f3359cb92767b409acaec9e4faa1e", size = 45135, upload-time = "2025-06-14T20:57:56.601Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/b6/a17fff77ed952414db65d1c589ea65ed5eba96febe59c27bf8acd1b2690d/pyobjc_framework_metalkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0be12860e68d960631bba4704b82670e4964191b5a20dbb48b4e1d840553ea9", size = 8715, upload-time = "2025-06-14T20:51:55.942Z" },
{ url = "https://files.pythonhosted.org/packages/2a/eb/fd5640015fc91b16e23cafe3a84508775344cd13f621e62b9c32d1750a83/pyobjc_framework_metalkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:95abb993d17be7a9d1174701594cc040e557983d0a0e9f49b1dfa9868ef20ed6", size = 8711, upload-time = "2025-06-14T20:51:56.765Z" },
{ url = "https://files.pythonhosted.org/packages/87/0c/516b6d7a67a170b7d2316701d5288797a19dd283fcc2f73b7b78973e1392/pyobjc_framework_metalkit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4854cf74fccf6ce516b49bf7cf8fc7c22da9a3743914a2f4b00f336206ad47ec", size = 8730, upload-time = "2025-06-14T20:51:57.824Z" },
{ url = "https://files.pythonhosted.org/packages/11/2a/5c55d1e57d8e90613fbce4b204b7d94a9ae7019a0928cb50cbd60bfa8191/pyobjc_framework_metalkit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:62e261b7798b276fee1fee065030a5d19d173863e9c697a80d1fc9a22258ec2c", size = 8749, upload-time = "2025-06-14T20:51:58.538Z" },
@@ -2472,7 +2344,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/d0/11/5df398a158a6efe2c87ac5cae121ef2788242afe5d4302d703147b9fcd91/pyobjc_framework_metalperformanceshaders-11.1.tar.gz", hash = "sha256:8a312d090a0f51651e63d9001e6cc7c1aa04ceccf23b494cbf84b7fd3d122071", size = 302113, upload-time = "2025-06-14T20:57:57.407Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2b/57/39eebff0c4428053fa4d81403a98c88ec04e896faf4af43964f91b18d33b/pyobjc_framework_metalperformanceshaders-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:045efaf395f7f08380a2a16cd21d75a7c295edb0311728cf37133b6c1842f1ec", size = 32652, upload-time = "2025-06-14T20:52:03.546Z" },
{ url = "https://files.pythonhosted.org/packages/64/ce/bbcf26f8aa94fb6edcf1a71ef23cd8df2afd4b5c2be451432211827c2ab0/pyobjc_framework_metalperformanceshaders-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:81ec1f85c55d11529008e6a0fb1329d5184620f04d89751c11bf14d7dd9798ee", size = 32650, upload-time = "2025-06-14T20:52:04.451Z" },
{ url = "https://files.pythonhosted.org/packages/89/df/f844516a54ef0fa1d047fe5fd94b63bc8b1218c09f7d4309b2a67a79708d/pyobjc_framework_metalperformanceshaders-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:06b2a4e446fe859e30f7efc7ccfbaefd443225a6ec53d949a113a6a4acc16c4c", size = 32888, upload-time = "2025-06-14T20:52:05.225Z" },
{ url = "https://files.pythonhosted.org/packages/b5/a2/5387ab012a20afb7252b3938a8fb5319c946a3faaa9166b79b51ab3c0bf6/pyobjc_framework_metalperformanceshaders-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:97be4bd0ded06c663205bd1cf821e148352346f147da48dba44cf7680f0ea23b", size = 32903, upload-time = "2025-06-14T20:52:06.31Z" },
@@ -2504,7 +2375,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/bd/48/8ae969a51a91864000e39c1de74627b12ff587b1dbad9406f7a30dfe71f8/pyobjc_framework_metrickit-11.1.tar.gz", hash = "sha256:a79d37575489916c35840e6a07edd958be578d3be7a3d621684d028d721f0b85", size = 40952, upload-time = "2025-06-14T20:57:58.996Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/82/eb/72894edce72ce8243f7f79bc2eed3abe4a480f6ab74def5db35d3f56c2b2/pyobjc_framework_metrickit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:41896afbbaf6ad817b3f1c595f4c728026bf04a0e0adaafc5157b3a4d078cb76", size = 8068, upload-time = "2025-06-14T20:52:11.53Z" },
{ url = "https://files.pythonhosted.org/packages/3b/cd/e459511c194d25c4acd31cbdb5c118215795785840861d55dbc8bd55cf35/pyobjc_framework_metrickit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a5d2b394f7acadd17d8947d188106424f59393b45dd4a842ac3cc50935170e3e", size = 8063, upload-time = "2025-06-14T20:52:12.696Z" },
{ url = "https://files.pythonhosted.org/packages/55/d1/aea4655e7eaa9ab19da8fe78ab363270443059c8a542b8f8a071b4988b57/pyobjc_framework_metrickit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a034e6b982e915da881edef87d71b063e596511d52aef7a32c683571f364156e", size = 8081, upload-time = "2025-06-14T20:52:13.72Z" },
{ url = "https://files.pythonhosted.org/packages/d9/d2/1f70e7524f6aca2e7aa7a99c4024d8c7e7cdd2ae9b338d2958548ee432c0/pyobjc_framework_metrickit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:95e98e96b8f122b0141e84f13ae9e0f91d09d0803b1c093fdc7d19123f000f9e", size = 8104, upload-time = "2025-06-14T20:52:14.405Z" },
@@ -2537,7 +2407,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/a0/27/140bf75706332729de252cc4141e8c8afe16a0e9e5818b5a23155aa3473c/pyobjc_framework_modelio-11.1.tar.gz", hash = "sha256:fad0fa2c09d468ac7e49848e144f7bbce6826f2178b3120add8960a83e5bfcb7", size = 123203, upload-time = "2025-06-14T20:58:01.035Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8a/65/c740598717774dd6ce71da753b901f784a22cf5267eefffacecafe4cf70c/pyobjc_framework_modelio-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:deb2d703f092a6f2b0b7d5044b0c3825a4c2c2f068f38bc2052a76e93f777bb0", size = 20143, upload-time = "2025-06-14T20:52:20.638Z" },
{ url = "https://files.pythonhosted.org/packages/6c/66/8109e52c7d97a108d4852a2032c9d7a7ecd27c6085bd7b2920b2ab575df4/pyobjc_framework_modelio-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4365fb96eb42b71c12efdfa2ff9d44755d5c292b8d1c78b947833d84271e359f", size = 20142, upload-time = "2025-06-14T20:52:21.582Z" },
{ url = "https://files.pythonhosted.org/packages/18/84/5f223b82894777388ef1aa09579d9c044044877a72075213741c97adc901/pyobjc_framework_modelio-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5d5e11389bde0852490b2a37896aaf9eb674b2a3586f2c572f9101cecb7bc576", size = 20172, upload-time = "2025-06-14T20:52:22.327Z" },
{ url = "https://files.pythonhosted.org/packages/00/8b/7c8b93d99d2102800834011f58d6e5cbb56d24c112c2e45c4730b103e4a3/pyobjc_framework_modelio-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:34fabde55d28aa8a12dd4476ad40182513cf87ee2fa928043aa6702961de302b", size = 20182, upload-time = "2025-06-14T20:52:23.063Z" },
@@ -2556,7 +2425,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/73/99/75bf6170e282d9e546b353b65af7859de8b1b27ddc431fc4afbf15423d01/pyobjc_framework_multipeerconnectivity-11.1.tar.gz", hash = "sha256:a3dacca5e6e2f1960dd2d1107d98399ff81ecf54a9852baa8ec8767dbfdbf54b", size = 26149, upload-time = "2025-06-14T20:58:01.793Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/69/bd/ef9dd2433b5ce453ef0ab4c34456bd5b9e248cf26cf988c283c2d976d6b5/pyobjc_framework_multipeerconnectivity-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:092fc396d235a8f3513b2ba4f8ff35fbd325d858eb9babe4df9c07d063ed647e", size = 11959, upload-time = "2025-06-14T20:52:27.533Z" },
{ url = "https://files.pythonhosted.org/packages/8d/fc/a3fc2514879a39673202f7ea5e835135255c5e510d30c58a43239ec1d9e0/pyobjc_framework_multipeerconnectivity-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b3c9d4d36e0c142b4ce91033740ed5bca19fe7ec96870d90610d2942ecd3cd39", size = 11955, upload-time = "2025-06-14T20:52:28.392Z" },
{ url = "https://files.pythonhosted.org/packages/b4/fe/5c29c227f6ed81147ec6ec3e681fc680a7ffe0360f96901371435ea68570/pyobjc_framework_multipeerconnectivity-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:970031deb3dbf8da1fcb04e785d4bd2eeedae8f6677db92881df6d92b05c31d6", size = 11981, upload-time = "2025-06-14T20:52:29.406Z" },
{ url = "https://files.pythonhosted.org/packages/d3/ea/f8d928235a67feeefec80e1f679bdb0c05f94e718a9aa22b4968ad65c6d1/pyobjc_framework_multipeerconnectivity-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c92c95ea611d5272ab37fd73bc8e68c3d8fde515a75b97d8b22dafa8acbc7daf", size = 11992, upload-time = "2025-06-14T20:52:30.148Z" },
@@ -2601,7 +2469,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/0a/ee/5ea93e48eca341b274027e1532bd8629fd55d609cd9c39c2c3acf26158c3/pyobjc_framework_network-11.1.tar.gz", hash = "sha256:f6df7a58a1279bbc976fd7e2efe813afbbb18427df40463e6e2ee28fba07d2df", size = 124670, upload-time = "2025-06-14T20:58:05.491Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8c/e0/7158d014e1545c2dce01cfdb7ad50ddcddd3f1f7303985def82f5f2a9f65/pyobjc_framework_network-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:53469903051aafbdd099c57c75b825f04167f1e3889634806af2bb762081d704", size = 19502, upload-time = "2025-06-14T20:52:36.282Z" },
{ url = "https://files.pythonhosted.org/packages/17/e9/a54f32daa0365bf000b739fc386d4783432273a9075337aa57a3808af65d/pyobjc_framework_network-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e56691507584c09cdb50f1cd69b5f57b42fd55c396e8c34fab8c5b81b44d36ed", size = 19499, upload-time = "2025-06-14T20:52:37.158Z" },
{ url = "https://files.pythonhosted.org/packages/15/c2/3c6626fdb3616fde2c173d313d15caea22d141abcc2fbf3b615f8555abe3/pyobjc_framework_network-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8cdc9be8ec3b0ae95e5c649e4bbcdf502cffd357dacc566223be707bdd5ac271", size = 19513, upload-time = "2025-06-14T20:52:38.423Z" },
{ url = "https://files.pythonhosted.org/packages/91/96/0824455bab6d321ccb5a38907ab8593e1c83b283ec850abee494278f1c96/pyobjc_framework_network-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:04582fef567392c2a10dcee9519356b79b17ab73ded050d14592da938d95b01a", size = 19537, upload-time = "2025-06-14T20:52:39.181Z" },
@@ -2620,7 +2487,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/71/30/d1eee738d702bbca78effdaa346a2b05359ab8a96d961b7cb44838e236ca/pyobjc_framework_networkextension-11.1.tar.gz", hash = "sha256:2b74b430ca651293e5aa90a1e7571b200d0acbf42803af87306ac8a1c70b0d4b", size = 217252, upload-time = "2025-06-14T20:58:06.311Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/77/ae/d79cdd1fbf5742f67aeba821e24b15b24756a4f0dabebd5ce284aa0e5cde/pyobjc_framework_networkextension-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7a679a2b17038de2fc3d66fce68361fb8152bd4e18cf95c15ccdbdef83d9da74", size = 14107, upload-time = "2025-06-14T20:52:43.623Z" },
{ url = "https://files.pythonhosted.org/packages/76/d7/b10aa191d37900ade78f1b7806d17ff29fa95f40ce7aeecce6f15ec94ac9/pyobjc_framework_networkextension-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:55e5ca70c81a864896b603cfcabf4c065783f64395460d16fe16db2bf0866d60", size = 14101, upload-time = "2025-06-14T20:52:44.527Z" },
{ url = "https://files.pythonhosted.org/packages/b6/26/526cd9f63e390e9c2153c41dc0982231b0b1ca88865deb538b77e1c3513d/pyobjc_framework_networkextension-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:853458aae8b43634461f6c44759750e2dc784c9aba561f9468ab14529b5a7fbe", size = 14114, upload-time = "2025-06-14T20:52:45.274Z" },
{ url = "https://files.pythonhosted.org/packages/06/30/ab050541fda285e2ce6b6ba0f1f5215809bd5ec75f71de8057ff8135737a/pyobjc_framework_networkextension-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d3d6e9810cb01c3a8f99aed5ee2d75f6f785204338b99b32e5f64370a18cc9dd", size = 14128, upload-time = "2025-06-14T20:52:46.328Z" },
@@ -2639,7 +2505,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/a8/4a/d3529b9bd7aae2c89d258ebc234673c5435e217a5136abd8c0aba37b916b/pyobjc_framework_notificationcenter-11.1.tar.gz", hash = "sha256:0b938053f2d6b1cea9db79313639d7eb9ddd5b2a5436a346be0887e75101e717", size = 23389, upload-time = "2025-06-14T20:58:07.136Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6f/87/8728579f85d2b9aeaf16cc305a0ffcc6f182872ae4685ea1fd687f9f7cf7/pyobjc_framework_notificationcenter-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:70704ba076eb30a2e25fd9d738d4ed2cf4a684c87a9129b1fc0c570301f53eee", size = 9859, upload-time = "2025-06-14T20:52:50.549Z" },
{ url = "https://files.pythonhosted.org/packages/ea/ed/3beb825e2b80de45b90e7cd510ad52890ac4a5a4de88cd9a5291235519fb/pyobjc_framework_notificationcenter-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d44413818e7fa3662f784cdcf0730c86676dd7333b7d24a7da13d4ffcde491b", size = 9859, upload-time = "2025-06-14T20:52:51.744Z" },
{ url = "https://files.pythonhosted.org/packages/6d/92/cd00fe5e54a191fb77611fe728a8c8a0a6edb229857d32f27806582406ca/pyobjc_framework_notificationcenter-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:65fc67374a471890245c7a1d60cf67dcf160075a9c048a5d89608a8290f33b03", size = 9880, upload-time = "2025-06-14T20:52:52.406Z" },
{ url = "https://files.pythonhosted.org/packages/40/e4/1bc444c5ee828a042e951c264ce597207e192fb6701c380db5ba05486955/pyobjc_framework_notificationcenter-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f5ce98882e301adef07651ba495ddd57b661d4c0398afd39f4591c1b44673cca", size = 9895, upload-time = "2025-06-14T20:52:53.105Z" },
@@ -2686,7 +2551,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/79/93/3feb7f6150b50165524750a424f5434448392123420cb4673db766c3f54a/pyobjc_framework_oslog-11.1.tar.gz", hash = "sha256:b2af409617e6b68fa1f1467c5a5679ebf59afd0cdc4b4528e1616059959a7979", size = 24689, upload-time = "2025-06-14T20:58:09.739Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7c/39/b433b6c9ff2aeeb616a65eef97f2a34c106629b5e801b2bfa98072905fae/pyobjc_framework_oslog-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d064b4ed8960bb65a277af16938043ebb4fb1d38fd47129bc9b9aeb6d385d4bc", size = 7792, upload-time = "2025-06-14T20:52:58.341Z" },
{ url = "https://files.pythonhosted.org/packages/66/7a/2db26fc24e16c84312a0de432bab16ca586223fd6c5ba08e49c192ae95f6/pyobjc_framework_oslog-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5dab25ef1cde4237cd2957c1f61c2888968e924304f7b9d9699eceeb330e9817", size = 7793, upload-time = "2025-06-14T20:52:59.132Z" },
{ url = "https://files.pythonhosted.org/packages/40/da/fd3bd62899cd679743056aa2c28bc821c2688682a17ddde1a08d6d9d67fc/pyobjc_framework_oslog-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7ae29c31ce51c476d3a37ca303465dd8bdfa98df2f6f951cf14c497e984a1ba9", size = 7799, upload-time = "2025-06-14T20:52:59.935Z" },
{ url = "https://files.pythonhosted.org/packages/9d/a9/d26bb3ec7ab2a3ef843c1697b6084dbd4a4a98d90ff8e29f4c227ade425e/pyobjc_framework_oslog-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7174ca2cdc073e555d5f5aea3baa7410c61a83a3741eaec23e8581340037680e", size = 7811, upload-time = "2025-06-14T20:53:00.621Z" },
@@ -2705,7 +2569,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/5c/05/063db500e7df70e39cbb5518a5a03c2acc06a1ca90b057061daea00129f3/pyobjc_framework_passkit-11.1.tar.gz", hash = "sha256:d2408b58960fca66607b483353c1ffbd751ef0bef394a1853ec414a34029566f", size = 144859, upload-time = "2025-06-14T20:58:10.761Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2b/82/fa43e676765c5dd2e4612fe2353127248d5ff8f56220476c8c6f64f34f54/pyobjc_framework_passkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f195a9c7d0ad46c975d22a0e3362ea6ccdb01e4cb1f81221db1037aee8225ff", size = 13945, upload-time = "2025-06-14T20:53:04.117Z" },
{ url = "https://files.pythonhosted.org/packages/80/18/343eb846e62704fbd64e178e0cbf75b121955c1973bf51ddd0871a42910a/pyobjc_framework_passkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:67b7b1ee9454919c073c2cba7bdba444a766a4e1dd15a5e906f4fa0c61525347", size = 13949, upload-time = "2025-06-14T20:53:04.98Z" },
{ url = "https://files.pythonhosted.org/packages/9d/ba/9e52213e0c0100079e4ef397cf4fd5ba8939fa4de19339755d1a373407a8/pyobjc_framework_passkit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:779eaea4e1931cfda4c8701e1111307b14bf9067b359a319fc992b6848a86932", size = 13959, upload-time = "2025-06-14T20:53:05.694Z" },
{ url = "https://files.pythonhosted.org/packages/d1/4f/e29dc665382e22cd6b4ebb1c5707a1b2059018a6462c81a7c344a9c40dba/pyobjc_framework_passkit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6306dda724ca812dca70154d40f32ec9bbdaff765a12f3cc45391723efe147e", size = 13971, upload-time = "2025-06-14T20:53:06.413Z" },
@@ -2750,7 +2613,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/78/b0/576652ecd05c26026ab4e75e0d81466edd570d060ce7df3d6bd812eb90d0/pyobjc_framework_photos-11.1.tar.gz", hash = "sha256:c8c3b25b14a2305047f72c7c081ff3655b3d051f7ed531476c03246798f8156d", size = 92569, upload-time = "2025-06-14T20:58:12.939Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ff/e8/6f46ea733d83c16c6782b84e3c2942c543c4370f03a408f40446bc8d1d7d/pyobjc_framework_photos-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1cd54a6b60a7ad2f810c02ec2c4d676feec4a25d08c9328ff839034b29c15bf7", size = 12200, upload-time = "2025-06-14T20:53:12.732Z" },
{ url = "https://files.pythonhosted.org/packages/df/25/ec3b0234d20948816791399e580f6dd83c0d50a24219c954708f755742c4/pyobjc_framework_photos-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:959dfc82f20513366b85cd37d8541bb0a6ab4f3bfa2f8094e9758a5245032d67", size = 12198, upload-time = "2025-06-14T20:53:13.563Z" },
{ url = "https://files.pythonhosted.org/packages/fa/24/2400e6b738d3ed622c61a7cc6604eec769f398071a1eb6a16dfdf3a9ceea/pyobjc_framework_photos-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8dbfffd29cfa63a8396ede0030785c15a5bc36065d3dd98fc6176a59e7abb3d3", size = 12224, upload-time = "2025-06-14T20:53:14.793Z" },
{ url = "https://files.pythonhosted.org/packages/70/60/cc575ee4287b250a42406e9b335f3293840996a840152cf93d1ce73790c5/pyobjc_framework_photos-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:541d8fafdb2f111f2f298e1aa0542f2d5871ce1dd481c3e9be4ed33916b38c3a", size = 12241, upload-time = "2025-06-14T20:53:15.469Z" },
@@ -2769,7 +2631,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/20/bb/e6de720efde2e9718677c95c6ae3f97047be437cda7a0f050cd1d6d2a434/pyobjc_framework_photosui-11.1.tar.gz", hash = "sha256:1c7ffab4860ce3e2b50feeed4f1d84488a9e38546db0bec09484d8d141c650df", size = 48443, upload-time = "2025-06-14T20:58:13.626Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b9/db/62d018110fa84ed12f07d7671376c034369db5383702dc27d9cabc179bdd/pyobjc_framework_photosui-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c2648031c62c30089ac8170a63ffbe92e6469447a488590504edd94cd51fd45a", size = 11672, upload-time = "2025-06-14T20:53:19.033Z" },
{ url = "https://files.pythonhosted.org/packages/af/c1/3d67c2af53fe91feb6f64dbc501bbcfd5d325b7f0f0ffffd5d033334cb03/pyobjc_framework_photosui-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d93722aeb8c134569035fd7e6632d0247e1bcb18c3cc4e0a288664218f241b85", size = 11667, upload-time = "2025-06-14T20:53:20.464Z" },
{ url = "https://files.pythonhosted.org/packages/f8/c1/a5c84c1695e7a066743d63d10b219d94f3c07d706871682e42f7db389f5c/pyobjc_framework_photosui-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b2f278f569dfd596a32468351411518a651d12cb91e60620094e852c525a5f10", size = 11682, upload-time = "2025-06-14T20:53:21.162Z" },
{ url = "https://files.pythonhosted.org/packages/33/10/506af430a9e7d356302b6bbee6672e03a4dfbc9a2f3a90fa79607d06387d/pyobjc_framework_photosui-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f0fa9c9e363c0db54957dfe4e26214379f2698caaba1e4ff4c9e3eba5e690d9", size = 11697, upload-time = "2025-06-14T20:53:21.855Z" },
@@ -2801,7 +2662,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/9f/f0/92d0eb26bf8af8ebf6b5b88df77e70b807de11f01af0162e0a429fcfb892/pyobjc_framework_pushkit-11.1.tar.gz", hash = "sha256:540769a4aadc3c9f08beca8496fe305372501eb28fdbca078db904a07b8e10f4", size = 21362, upload-time = "2025-06-14T20:58:15.642Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2d/ce/e67405c33db03c2255775afede730f85ff9d88ebf9d8b838ee20bba648ae/pyobjc_framework_pushkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:48c38a7d3bef449c23aa799b70283586e0b7d9203cf17b0666bc61278b663ed2", size = 8150, upload-time = "2025-06-14T20:53:27.254Z" },
{ url = "https://files.pythonhosted.org/packages/d9/dc/415d6d7e3ed04d8b2f8dc6d458e7c6db3f503737b092d71b4856bf1607f7/pyobjc_framework_pushkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5e2f08b667035df6b11a0a26f038610df1eebbedf9f3f111c241b5afaaf7c5fd", size = 8149, upload-time = "2025-06-14T20:53:28.096Z" },
{ url = "https://files.pythonhosted.org/packages/31/65/260014c5d13c54bd359221b0a890cbffdb99eecff3703f253cf648e45036/pyobjc_framework_pushkit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:21993b7e9127b05575a954faa68e85301c6a4c04e34e38aff9050f67a05c562a", size = 8174, upload-time = "2025-06-14T20:53:28.805Z" },
{ url = "https://files.pythonhosted.org/packages/b4/b2/08514fa6be83a359bb6d72f9009f17f16f7efc0fe802029d1f6f0c4fc5c9/pyobjc_framework_pushkit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bac3ee77dfbe936998f207c1579e346993485bab8849db537ed250261cf12ab3", size = 8190, upload-time = "2025-06-14T20:53:29.651Z" },
@@ -2820,7 +2680,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/c7/ac/6308fec6c9ffeda9942fef72724f4094c6df4933560f512e63eac37ebd30/pyobjc_framework_quartz-11.1.tar.gz", hash = "sha256:a57f35ccfc22ad48c87c5932818e583777ff7276605fef6afad0ac0741169f75", size = 3953275, upload-time = "2025-06-14T20:58:17.924Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b9/62/f8d9bb4cba92d5f220327cf1def2c2c5be324880d54ee57e7bea43aa28b2/pyobjc_framework_quartz-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b5ef75c416b0209e25b2eb07a27bd7eedf14a8c6b2f968711969d45ceceb0f84", size = 215586, upload-time = "2025-06-14T20:53:34.018Z" },
{ url = "https://files.pythonhosted.org/packages/77/cb/38172fdb350b3f47e18d87c5760e50f4efbb4da6308182b5e1310ff0cde4/pyobjc_framework_quartz-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2d501fe95ef15d8acf587cb7dc4ab4be3c5a84e2252017da8dbb7df1bbe7a72a", size = 215565, upload-time = "2025-06-14T20:53:35.262Z" },
{ url = "https://files.pythonhosted.org/packages/9b/37/ee6e0bdd31b3b277fec00e5ee84d30eb1b5b8b0e025095e24ddc561697d0/pyobjc_framework_quartz-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9ac806067541917d6119b98d90390a6944e7d9bd737f5c0a79884202327c9204", size = 216410, upload-time = "2025-06-14T20:53:36.346Z" },
{ url = "https://files.pythonhosted.org/packages/bd/27/4f4fc0e6a0652318c2844608dd7c41e49ba6006ee5fb60c7ae417c338357/pyobjc_framework_quartz-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43a1138280571bbf44df27a7eef519184b5c4183a588598ebaaeb887b9e73e76", size = 216816, upload-time = "2025-06-14T20:53:37.358Z" },
@@ -2853,7 +2712,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/c8/4f/014e95f0fd6842d7fcc3d443feb6ee65ac69d06c66ffa9327fc33ceb7c27/pyobjc_framework_replaykit-11.1.tar.gz", hash = "sha256:6919baa123a6d8aad769769fcff87369e13ee7bae11b955a8185a406a651061b", size = 26132, upload-time = "2025-06-14T20:58:21.853Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8f/b0/095e0dd648e23ef5d3b175813759e37bd19ad8885ca55d467af833cddb51/pyobjc_framework_replaykit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:634b18c7b0f2ea548421307d6c59339d69094dfde9b638ce0ca3d6d3016de470", size = 10063, upload-time = "2025-06-14T20:53:44.374Z" },
{ url = "https://files.pythonhosted.org/packages/72/97/2b4fbd52c6727977c0fdbde2b4a15226a9beb836248c289781e4129394e4/pyobjc_framework_replaykit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d88c3867349865d8a3a06ea064f15aed7e5be20d22882ac8a647d9b6959594e", size = 10066, upload-time = "2025-06-14T20:53:45.555Z" },
{ url = "https://files.pythonhosted.org/packages/b9/73/846cebb36fc279df18f10dc3a27cba8fe2e47e95350a3651147e4d454719/pyobjc_framework_replaykit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:22c6d09be9a6e758426d723a6c3658ad6bbb66f97ba9a1909bfcf29a91d99921", size = 10087, upload-time = "2025-06-14T20:53:46.242Z" },
{ url = "https://files.pythonhosted.org/packages/bf/2e/996764cd045b6c9e033167e573c9fe67c4e867eb6ab49c2d4fde005cd4a7/pyobjc_framework_replaykit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7742ee18c8c9b61f5668698a05b88d25d34461fcdd95a8f669ecdfd8db8c4d42", size = 10108, upload-time = "2025-06-14T20:53:47.293Z" },
@@ -2872,7 +2730,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/1a/fc/c47d2abf3c1de6db21d685cace76a0931d594aa369e3d090260295273f6e/pyobjc_framework_safariservices-11.1.tar.gz", hash = "sha256:39a17df1a8e1c339457f3acbff0dc0eae4681d158f9d783a11995cf484aa9cd0", size = 34905, upload-time = "2025-06-14T20:58:22.492Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e6/6d/e77dbad597911dca833a92d7dc1f280b0e6eff3d936a59ee3cce28015299/pyobjc_framework_safariservices-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:62e70805477b04d1abc6dfa1f22d2ee41af8a5784fa98d3dcbd9fca00b6dd521", size = 7266, upload-time = "2025-06-14T20:53:51.144Z" },
{ url = "https://files.pythonhosted.org/packages/c9/aa/0c9f3456a57dbee711210a0ac3fe58aff9bf881ab7c65727b885193eb8af/pyobjc_framework_safariservices-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a441a2e99f7d6475bea00c3d53de924143b8f90052be226aee16f1f6d9cfdc8c", size = 7262, upload-time = "2025-06-14T20:53:52.057Z" },
{ url = "https://files.pythonhosted.org/packages/d7/13/9636e9d3dc362daaaa025b2aa4e28606a1e197dfc6506d3a246be8315f8a/pyobjc_framework_safariservices-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c92eb9e35f98368ea1bfaa8cdd41138ca8b004ea5a85833390a44e5626ca5061", size = 7275, upload-time = "2025-06-14T20:53:53.075Z" },
{ url = "https://files.pythonhosted.org/packages/de/cd/9ed0083373be3bf6da2450a6800b54965fea95b2452473ee0e36ddc72573/pyobjc_framework_safariservices-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8b4d4169dd21e69246d90a42f872b7148064b63de6bbbf6bc6ddabe33f143843", size = 7290, upload-time = "2025-06-14T20:53:53.816Z" },
@@ -2892,7 +2749,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/28/cc/f6aa5d6f45179bd084416511be4e5b0dd0752cb76daa93869e6edb806096/pyobjc_framework_safetykit-11.1.tar.gz", hash = "sha256:c6b44e0cf69e27584ac3ef3d8b771d19a7c2ccd9c6de4138d091358e036322d4", size = 21240, upload-time = "2025-06-14T20:58:23.132Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f2/b1/2fe277f52a026e3239ad3306a029dc206eb37c448a0a254a9ffa4f619429/pyobjc_framework_safetykit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:45c1fb59246ca9eef99149f3b325491a1aec7f775dd136f6de86aa69911cc43f", size = 8513, upload-time = "2025-06-14T20:53:57.626Z" },
{ url = "https://files.pythonhosted.org/packages/a3/ad/1e9c661510cc4cd96f2beffc7ba39af36064c742e265303c689e85aaa0ad/pyobjc_framework_safetykit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3333e8e53a1e8c8133936684813a2254e5d1b4fe313333a3d0273e31b9158cf7", size = 8513, upload-time = "2025-06-14T20:53:58.413Z" },
{ url = "https://files.pythonhosted.org/packages/9c/8f/6f4c833e31526a81faef9bf19695b332ba8d2fa53d92640abd6fb3ac1d78/pyobjc_framework_safetykit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b76fccdb970d3d751a540c47712e9110afac9abea952cb9b7bc0d5867db896e3", size = 8523, upload-time = "2025-06-14T20:53:59.443Z" },
{ url = "https://files.pythonhosted.org/packages/85/3d/782e1738f2eb4b276baabd85a8b263bf75b2c4e990fd5950eeadfb59ebeb/pyobjc_framework_safetykit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8130de57f701dbccb1d84c76ec007fe04992da58cbf0eb906324393eeac3d08d", size = 8541, upload-time = "2025-06-14T20:54:00.461Z" },
@@ -2912,7 +2768,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/64/cf/2d89777120d2812e7ee53c703bf6fc8968606c29ddc1351bc63f0a2a5692/pyobjc_framework_scenekit-11.1.tar.gz", hash = "sha256:82941f1e5040114d6e2c9fd35507244e102ef561c637686091b71a7ad0f31306", size = 214118, upload-time = "2025-06-14T20:58:24.003Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ea/32/b4729d52b23d2380e63fadd7587c3fe73f8e7a9a39afa022dd71f14b2550/pyobjc_framework_scenekit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c828200919573e1c5a02f8702b2e0f8a6c46edddd2d690666d8cf16575f4578", size = 33495, upload-time = "2025-06-14T20:54:03.953Z" },
{ url = "https://files.pythonhosted.org/packages/51/46/d011b5a88e45d78265f5df144759ff57e50d361d44c9adb68c2fb58b276d/pyobjc_framework_scenekit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3e777dacb563946ad0c2351e6cfe3f16b8587a65772ec0654e2be9f75764d234", size = 33490, upload-time = "2025-06-14T20:54:04.845Z" },
{ url = "https://files.pythonhosted.org/packages/e0/f9/bdcd8a4bc6c387ef07f3e2190cea6a03d4f7ed761784f492b01323e8d900/pyobjc_framework_scenekit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c803d95b30c4ce49f46ff7174806f5eb84e4c3a152f8f580c5da0313c5c67041", size = 33558, upload-time = "2025-06-14T20:54:05.59Z" },
{ url = "https://files.pythonhosted.org/packages/ce/5e/9bb308fd68b56a8cf9ea5213e6c988232ce6ae4e6ccd4cf53b38f0018deb/pyobjc_framework_scenekit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2f347d5ae42af8acddb86a45f965046bb91f8d83d33851390954439961e2a7b7", size = 33577, upload-time = "2025-06-14T20:54:06.69Z" },
@@ -2932,7 +2787,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/32/a5/9bd1f1ad1773a1304ccde934ff39e0f0a0b0034441bf89166aea649606de/pyobjc_framework_screencapturekit-11.1.tar.gz", hash = "sha256:11443781a30ed446f2d892c9e6642ca4897eb45f1a1411136ca584997fa739e0", size = 53548, upload-time = "2025-06-14T20:58:24.837Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/19/74/bf80d57082e449571d7122aedf87a6ee9870125c33eb9d7a09046ad06503/pyobjc_framework_screencapturekit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:11de78f270d405bd14b784b15d4bb04a13b3d25613abd5f9aaaf2b8ef108dc60", size = 11280, upload-time = "2025-06-14T20:54:10.829Z" },
{ url = "https://files.pythonhosted.org/packages/7e/e0/fd1957e962c4a1624171dbbda4e425615848a7bcc9b45a524018dc449874/pyobjc_framework_screencapturekit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7203108d28d7373501c455cd4a8bbcd2eb7849906dbc7859ac17a350b141553c", size = 11280, upload-time = "2025-06-14T20:54:11.699Z" },
{ url = "https://files.pythonhosted.org/packages/98/37/840f306dcf01dd2bd092ae8dcf371a3bad3a0f88f0780d0840f899a8c047/pyobjc_framework_screencapturekit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:641fa7834f54558859209e174c83551d5fa239ca6943ace52665f7d45e562ff2", size = 11308, upload-time = "2025-06-14T20:54:12.382Z" },
{ url = "https://files.pythonhosted.org/packages/1b/9e/de4c2e3ae834c2f60c9e78d95e1f2488b679b4cf74fa5bfba7f065fb827b/pyobjc_framework_screencapturekit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1119d6258d6c668564ab39154cfc745fd2bb8b3beeaa4f9b2a8a4c93926678c0", size = 11324, upload-time = "2025-06-14T20:54:13.104Z" },
@@ -2951,7 +2805,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/7c/f6/f2d48583b29fc67b64aa1f415fd51faf003d045cdb1f3acab039b9a3f59f/pyobjc_framework_screensaver-11.1.tar.gz", hash = "sha256:d5fbc9dc076cc574ead183d521840b56be0c160415e43cb8e01cfddd6d6372c2", size = 24302, upload-time = "2025-06-14T20:58:25.52Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5a/a6/656e2cfe80e76d9eb2f2dd3b0ebde46ee6da32f1c21dc8cdc582b8f579c3/pyobjc_framework_screensaver-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:656651d0b6870bffeea01b65f4748936603a62dbbdc8e7a61c125ea6ebf8299c", size = 8470, upload-time = "2025-06-14T20:54:17.498Z" },
{ url = "https://files.pythonhosted.org/packages/f0/8c/2236e5796f329a92ce7664036da91e91d63d86217972dc2939261ce88dde/pyobjc_framework_screensaver-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8b959761fddf06d9fb3fed6cd0cea6009d60473317e11490f66dcf0444011d5f", size = 8466, upload-time = "2025-06-14T20:54:18.329Z" },
{ url = "https://files.pythonhosted.org/packages/76/f9/4ae982c7a1387b64954130b72187e140329b73c647acb4d6b6eb3c033d8d/pyobjc_framework_screensaver-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f2d22293cf9d715e4692267a1678096afd6793c0519d9417cf77c8a6c706a543", size = 8402, upload-time = "2025-06-14T20:54:19.044Z" },
{ url = "https://files.pythonhosted.org/packages/dc/ff/c2e83551474d3c401181ce1d859ebd0e0b1986ab8ee932d647debebbe7eb/pyobjc_framework_screensaver-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:46d65c1e14d35f287e7be351e2f98daf9489e31e7ca0d306e6102904ce6c40fb", size = 8419, upload-time = "2025-06-14T20:54:19.741Z" },
@@ -2983,7 +2836,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/8e/c1/5b1dd01ff173df4c6676f97405113458918819cb2064c1735b61948e8800/pyobjc_framework_scriptingbridge-11.1.tar.gz", hash = "sha256:604445c759210a35d86d3e0dfcde0aac8e5e3e9d9e35759e0723952138843699", size = 23155, upload-time = "2025-06-14T20:58:26.812Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c7/93/44afb7fe0285cfaffb2dc78ea4c3cdf0365e866f18dbc21fa5c685d1efc3/pyobjc_framework_scriptingbridge-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2cf247dfe9f98aa3c8210395d045a708a4133a5d6164673213eb39afc4f6dd31", size = 8303, upload-time = "2025-06-14T20:54:27.725Z" },
{ url = "https://files.pythonhosted.org/packages/e6/76/e173ca0b121693bdc6ac5797b30fd5771f31a682d15fd46402dc6f9ca3d1/pyobjc_framework_scriptingbridge-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d6020c69c14872105852ff99aab7cd2b2671e61ded3faefb071dc40a8916c527", size = 8301, upload-time = "2025-06-14T20:54:29.082Z" },
{ url = "https://files.pythonhosted.org/packages/c1/64/31849063e3e81b4c312ce838dc98f0409c09eb33bc79dbb5261cb994a4c4/pyobjc_framework_scriptingbridge-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:226ba12d9cbd504411b702323b0507dd1690e81b4ce657c5f0d8b998c46cf374", size = 8323, upload-time = "2025-06-14T20:54:30.105Z" },
{ url = "https://files.pythonhosted.org/packages/d8/19/3003d4a137ce84fa8cb42a9c84f8c04e83c89749ab9cf93bc755016434b7/pyobjc_framework_scriptingbridge-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c2ba0ad3d3e4e3c6a43fe3e84ab02c5c4e74000bb6f130ae47bf82a3dcd4af98", size = 8337, upload-time = "2025-06-14T20:54:30.81Z" },
@@ -3015,7 +2867,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/ee/6f/ba50ed2d9c1192c67590a7cfefa44fc5f85c776d1e25beb224dec32081f6/pyobjc_framework_security-11.1.tar.gz", hash = "sha256:dabcee6987c6bae575e2d1ef0fcbe437678c4f49f1c25a4b131a5e960f31a2da", size = 302291, upload-time = "2025-06-14T20:58:28.506Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d2/f9/e3a96541e7fddac76e63dd73a88bbaecbc014787df95a49c609386563b75/pyobjc_framework_security-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ffe21933b554098709087fbc4e629ab4875e75d74ffb741de508063dba56c73e", size = 41207, upload-time = "2025-06-14T20:54:35.376Z" },
{ url = "https://files.pythonhosted.org/packages/ac/ae/1679770d9a1cf5f2fe532a3567a51f0c5ee09054ae2c4003ae8f3e11eea4/pyobjc_framework_security-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d361231697486e97cfdafadf56709190696ab26a6a086dbba5f170e042e13daa", size = 41202, upload-time = "2025-06-14T20:54:36.255Z" },
{ url = "https://files.pythonhosted.org/packages/35/16/7fc52ab1364ada5885bf9b4c9ea9da3ad892b847c9b86aa59e086b16fc11/pyobjc_framework_security-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2eb4ba6d8b221b9ad5d010e026247e8aa26ee43dcaf327e848340ed227d22d7e", size = 41222, upload-time = "2025-06-14T20:54:37.032Z" },
{ url = "https://files.pythonhosted.org/packages/3f/d8/cb20b4c4d15b2bdc7e39481159e50a933ddb87e4702d35060c254b316055/pyobjc_framework_security-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:158da3b2474e2567fd269531c4ee9f35b8ba4f1eccbd1fb4a37c85a18bf1243c", size = 41221, upload-time = "2025-06-14T20:54:37.803Z" },
@@ -3049,7 +2900,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/a1/be/c846651c3e7f38a637c40ae1bcda9f14237c2395637c3a188df4f733c727/pyobjc_framework_securityinterface-11.1.tar.gz", hash = "sha256:e7aa6373e525f3ae05d71276e821a6348c53fec9f812b90eec1dbadfcb507bc9", size = 37648, upload-time = "2025-06-14T20:58:29.932Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cf/c2/dcbdfb6954d3df13e5a48d0344c17a2b5dbc2bc6eeef6342d523b33e3f28/pyobjc_framework_securityinterface-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:127da21b8fd4d8df0f1d680f581cef714eeb8c2db31e72b2c5395e2ad41936ff", size = 10775, upload-time = "2025-06-14T20:54:42.532Z" },
{ url = "https://files.pythonhosted.org/packages/1c/ec/8073f37f56870efb039970f1cc4536f279c5d476abab2e8654129789277f/pyobjc_framework_securityinterface-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3e884620b22918d462764f0665f6ac0cbb8142bb160fcd27c4f4357f81da73b7", size = 10769, upload-time = "2025-06-14T20:54:43.344Z" },
{ url = "https://files.pythonhosted.org/packages/6f/ab/48b8027a24f3f8924f5be5f97217961b4ed23e6be49b3bd94ee8a0d56a1e/pyobjc_framework_securityinterface-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:26056441b325029da06a7c7b8dd1a0c9a4ad7d980596c1b04d132a502b4cacc0", size = 10837, upload-time = "2025-06-14T20:54:44.052Z" },
{ url = "https://files.pythonhosted.org/packages/31/2e/de226a3caa47b4a800c8e6289b9fe30c71f10985dbc37379d5bd0781b470/pyobjc_framework_securityinterface-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:708dd1d65309f3d4043ecaf152591c240601a5d3da7ae7a500f511c54317537b", size = 10851, upload-time = "2025-06-14T20:54:45.254Z" },
@@ -3109,7 +2959,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/fe/a5/e299fbd0c13d4fac9356459f21372f6eef4279d0fbc99ba316d88dfbbfb4/pyobjc_framework_sharedwithyou-11.1.tar.gz", hash = "sha256:ece3a28a3083d0bcad0ac95b01f0eb699b9d2d0c02c61305bfd402678753ff6e", size = 34216, upload-time = "2025-06-14T20:58:32.75Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ed/ba/1defa14da058458d032d9fe4aadb93230c07b9a4feb620925d6803c5a183/pyobjc_framework_sharedwithyou-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2a218b3c89253a5c3a0ca974854872b68f58d46373a3e38ab20a82c9484a1062", size = 8725, upload-time = "2025-06-14T20:54:52.413Z" },
{ url = "https://files.pythonhosted.org/packages/2e/23/7caefaddc58702da830d1cc4eb3c45ae82dcd605ea362126ab47ebd54f7d/pyobjc_framework_sharedwithyou-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ce1c37d5f8cf5b0fe8a261e4e7256da677162fd5aa7b724e83532cdfe58d8f94", size = 8725, upload-time = "2025-06-14T20:54:53.179Z" },
{ url = "https://files.pythonhosted.org/packages/57/44/211e1f18676e85d3656671fc0c954ced2cd007e55f1b0b6b2e4d0a0852eb/pyobjc_framework_sharedwithyou-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:99e1749187ae370be7b9c55dd076d1b8143f0d8db3e83f52540586f32e7abb33", size = 8740, upload-time = "2025-06-14T20:54:53.879Z" },
{ url = "https://files.pythonhosted.org/packages/6f/da/1a2f2ae024e0206e1bcaba27aac2ebadf8bceb0ee05d03be2250e8c3d1a3/pyobjc_framework_sharedwithyou-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c1a1770aa2c417f17010623414fb12943570baa726d8780dd7446ba5bcee8c3d", size = 8759, upload-time = "2025-06-14T20:54:54.631Z" },
@@ -3128,7 +2977,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/79/a3/1ca6ff1b785772c7c5a38a7c017c6f971b1eda638d6a0aab3bbde18ac086/pyobjc_framework_sharedwithyoucore-11.1.tar.gz", hash = "sha256:790050d25f47bda662a9f008b17ca640ac2460f2559a56b17995e53f2f44ed73", size = 29459, upload-time = "2025-06-14T20:58:33.422Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4c/22/d5f9b49735c252b456b38637ed32de5abb7806d008bc2ffab5f8fa01b86e/pyobjc_framework_sharedwithyoucore-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:daa8de2cbf5ec8e768e4d8b7b7cd410747d92ca83ccf7d114563537448099136", size = 8494, upload-time = "2025-06-14T20:54:58.857Z" },
{ url = "https://files.pythonhosted.org/packages/7a/df/08cfa01dcdb4655514b7a10eb7c40da2bdb7866078c761d6ed26c9f464f7/pyobjc_framework_sharedwithyoucore-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a7fe5ffcc65093ef7cd25903769ad557c3d3c5a59155a31f3f934cf555101e6", size = 8489, upload-time = "2025-06-14T20:54:59.631Z" },
{ url = "https://files.pythonhosted.org/packages/b9/70/3b2e13fcf393aa434b1cf5c29c6aaf65ee5b8361254df3a920ed436bb5e4/pyobjc_framework_sharedwithyoucore-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dd18c588b29de322c25821934d6aa6d2bbbdbb89b6a4efacdb248b4115fc488d", size = 8512, upload-time = "2025-06-14T20:55:00.411Z" },
{ url = "https://files.pythonhosted.org/packages/b7/fc/feb2912fb9c7bbeb2099d2cb42ad28055c6e29504fcb92bd8a011fcba66a/pyobjc_framework_sharedwithyoucore-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a3fb0e745fd022fed48cc9a5e0dcbf8d1abcb5bfc192150e3a2584f4351791fc", size = 8527, upload-time = "2025-06-14T20:55:01.112Z" },
@@ -3147,7 +2995,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/de/08/ba739b97f1e441653bae8da5dd1e441bbbfa43940018d21edb60da7dd163/pyobjc_framework_shazamkit-11.1.tar.gz", hash = "sha256:c6e3c9ab8744d9319a89b78ae6f185bb5704efb68509e66d77bcd1f84a9446d6", size = 25797, upload-time = "2025-06-14T20:58:34.086Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/91/35/1324d24af276181b6bf60d322fe7a6d400469dfd16d51c156e338ac248c7/pyobjc_framework_shazamkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5f19e1f307d84c53271af7ed70a3c39f134a46e358672fb8c74ced7205949551", size = 8535, upload-time = "2025-06-14T20:55:05.116Z" },
{ url = "https://files.pythonhosted.org/packages/f8/b6/c03bc9aad7f15979b5d7f144baf5161c3c40e0bca194cce82e1bce0804a9/pyobjc_framework_shazamkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2fe6990d0ec1b40d4efd0d0e49c2deb65198f49b963e6215c608c140b3149151", size = 8540, upload-time = "2025-06-14T20:55:05.978Z" },
{ url = "https://files.pythonhosted.org/packages/89/b7/594b8bdc406603a7a07cdb33f2be483fed16aebc35aeb087385fc9eca844/pyobjc_framework_shazamkit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b323f5409b01711aa2b6e2113306084fab2cc83fa57a0c3d55bd5876358b68d8", size = 8560, upload-time = "2025-06-14T20:55:07.564Z" },
{ url = "https://files.pythonhosted.org/packages/8c/fa/49ba8d1f9e257a12267773d6682e170fba441c7ea72d6fe58da9f4bf6f10/pyobjc_framework_shazamkit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bac17f285742e0f13a54c7085ef3035d8034ffc43d18d3d68fb41283c5064ff", size = 8573, upload-time = "2025-06-14T20:55:08.42Z" },
@@ -3192,7 +3039,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/67/76/2a1fd7637b2c662349ede09806e159306afeebfba18fb062ad053b41d811/pyobjc_framework_speech-11.1.tar.gz", hash = "sha256:d382977208c3710eacea89e05eae4578f1638bb5a7b667c06971e3d34e96845c", size = 41179, upload-time = "2025-06-14T20:58:36.43Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/69/a7/ff17046ad2575b13e6d836ab1f446b8ad095ecffa29f650d5ab4e6b5441f/pyobjc_framework_speech-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5fcbe46060f0b25963e32fa7488a34fb3f929fa099797a10e30012d3d6ee328a", size = 9168, upload-time = "2025-06-14T20:55:14.135Z" },
{ url = "https://files.pythonhosted.org/packages/b5/d3/c3b1d542c5ddc816924f02edf2ececcda226f35c91e95ed80f2632fbd91c/pyobjc_framework_speech-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d3e0276a66d2fa4357959a6f6fb5def03f8e0fd3aa43711d6a81ab2573b9415f", size = 9171, upload-time = "2025-06-14T20:55:15.316Z" },
{ url = "https://files.pythonhosted.org/packages/78/59/267f4699055beb39723ccbff70909ec3851e4adf17386f6ad85e5d983780/pyobjc_framework_speech-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7726eff52cfa9cc7178ddcd1285cbc23b5f89ee55b4b850b0d2e90bb4f8e044b", size = 9180, upload-time = "2025-06-14T20:55:16.556Z" },
{ url = "https://files.pythonhosted.org/packages/ea/a6/c394c3973c42d86c7b0c5c673c5ce65d10671e59e174f1ba4e7ab61ae5df/pyobjc_framework_speech-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3c80670dbad921bf1d4954a9de29525acb53ee84e064a95fbbdfddff1db2f14f", size = 9198, upload-time = "2025-06-14T20:55:17.581Z" },
@@ -3212,7 +3058,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/16/02/2e253ba4f7fad6efe05fd5fcf44aede093f6c438d608d67c6c6623a1846d/pyobjc_framework_spritekit-11.1.tar.gz", hash = "sha256:914da6e846573cac8db5e403dec9a3e6f6edf5211f9b7e429734924d00f65108", size = 130297, upload-time = "2025-06-14T20:58:37.113Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ed/d0/5c63afd486f826164074ea54c4577dac4564a20b57c9ad9ba6db373b8229/pyobjc_framework_spritekit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5adddbeea27ca748d4fd4588ffa79299fb7a7b369038bc6e3425570d1cab9b0a", size = 17721, upload-time = "2025-06-14T20:55:21.627Z" },
{ url = "https://files.pythonhosted.org/packages/8f/83/1c874cffba691cf8c103e0fdf55b53d9749577794efb9fc30e4394ffef41/pyobjc_framework_spritekit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1c8c94d37c054b6e3c22c237f6458c12649776e5ac921d066ab99dee2e580909", size = 17718, upload-time = "2025-06-14T20:55:22.543Z" },
{ url = "https://files.pythonhosted.org/packages/f1/fe/39d92bf40ec7a6116f89fd95053321f7c00c50c10d82b9adfa0f9ebdb10c/pyobjc_framework_spritekit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8b470a890db69e70ef428dfff88da499500fca9b2d44da7120dc588d13a2dbdb", size = 17776, upload-time = "2025-06-14T20:55:23.639Z" },
{ url = "https://files.pythonhosted.org/packages/3f/c1/56490cce24e34e8c4c8c6a0f4746cd3a8bb5c2403e243c99f4dfa0cd147f/pyobjc_framework_spritekit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2277e74d7be426181ae5ca7dd9d6c776426e8e825ad83b6046a7cb999015f27d", size = 17798, upload-time = "2025-06-14T20:55:24.407Z" },
@@ -3231,7 +3076,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/44/a0/58cab9ebc9ac9282e1d4734b1987d1c3cd652b415ec3e678fcc5e735d279/pyobjc_framework_storekit-11.1.tar.gz", hash = "sha256:85acc30c0bfa120b37c3c5ac693fe9ad2c2e351ee7a1f9ea6f976b0c311ff164", size = 76421, upload-time = "2025-06-14T20:58:37.86Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9c/af/cf8a488c48d811bbb3ddacc98356162a2d20009fcfef4df69183d8c9d679/pyobjc_framework_storekit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:850b8157c30aa023c16a883a140538ca229d7b30db6c17568ea69532b19256ad", size = 11843, upload-time = "2025-06-14T20:55:28.875Z" },
{ url = "https://files.pythonhosted.org/packages/d4/30/7549a7bd2b068cd460792e09a66d88465aab2ac6fb2ddcf77b7bf5712eee/pyobjc_framework_storekit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:624105bd26a9ce5a097b3f96653e2700d33bb095828ed65ee0f4679b34d9f1e1", size = 11841, upload-time = "2025-06-14T20:55:29.735Z" },
{ url = "https://files.pythonhosted.org/packages/ac/61/6404aac6857ea43798882333bcc26bfd3c9c3a1efc7a575cbf3e53538e2a/pyobjc_framework_storekit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5ca3373272b6989917c88571ca170ce6d771180fe1a2b44c7643fe084569b93e", size = 11868, upload-time = "2025-06-14T20:55:30.454Z" },
{ url = "https://files.pythonhosted.org/packages/6b/52/23acdf128a5b04059b2a3b38928afbff0afb50da439b597e25cdff1e9148/pyobjc_framework_storekit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2e2607116b0d53d7fda2fc48e37b1deb1d26a60e7b723a6b7c391a3f48b2ac3b", size = 11882, upload-time = "2025-06-14T20:55:31.523Z" },
@@ -3264,7 +3108,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/69/45/cd9fa83ed1d75be7130fb8e41c375f05b5d6621737ec37e9d8da78676613/pyobjc_framework_syncservices-11.1.tar.gz", hash = "sha256:0f141d717256b98c17ec2eddbc983c4bd39dfa00dc0c31b4174742e73a8447fe", size = 57996, upload-time = "2025-06-14T20:58:39.146Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/38/4d/10df334b64f9380dcf118a512545e8346c4bbfd43df3b554c9fe8468a0b2/pyobjc_framework_syncservices-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:108619faf4cafb894022ca923b52d45008eb6ad3af2123ca4e187101a74ddaee", size = 13468, upload-time = "2025-06-14T20:55:36.226Z" },
{ url = "https://files.pythonhosted.org/packages/f5/7e/60e184beafca85571cfa68d46a8f453a54edbc7d2eceb18163cfec438438/pyobjc_framework_syncservices-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bc6159bda4597149c6999b052a35ffd9fc4817988293da6e54a1e073fa571653", size = 13464, upload-time = "2025-06-14T20:55:37.117Z" },
{ url = "https://files.pythonhosted.org/packages/01/2b/6d7d65c08a9c51eed12eb7f83eaa48deaed621036f77221b3b0346c3f6c2/pyobjc_framework_syncservices-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:03124c8c7c7ce837f51e1c9bdcf84c6f1d5201f92c8a1c172ec34908d5e57415", size = 13496, upload-time = "2025-06-14T20:55:37.83Z" },
{ url = "https://files.pythonhosted.org/packages/99/7b/88e89b81b5a6ee7da3b452c1619ec22936a8dd4384afd67f6019472655b8/pyobjc_framework_syncservices-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:711d493c7967682bee605c5909a49d268d9b3dd3cb7a71d8ab5dbe01a069eb44", size = 13511, upload-time = "2025-06-14T20:55:38.55Z" },
@@ -3283,7 +3126,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/e2/3d/41590c0afc72e93d911348fbde0c9c1071ff53c6f86df42df64b21174bb9/pyobjc_framework_systemconfiguration-11.1.tar.gz", hash = "sha256:f30ed0e9a8233fecb06522e67795918ab230ddcc4a18e15494eff7532f4c3ae1", size = 143410, upload-time = "2025-06-14T20:58:39.917Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5f/2d/61af34da4e8faa3bdd08ab29dc86f7b5eb5b67bcbc88c23da57513e39392/pyobjc_framework_systemconfiguration-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:45ede697a3f9d4f97f1554a3f5636197aee83923d3adbe0901935da8ddb559a9", size = 21736, upload-time = "2025-06-14T20:55:43.013Z" },
{ url = "https://files.pythonhosted.org/packages/64/9b/8fe26a9ac85898fa58f6206f357745ec44cd95b63786503ce05c382344ce/pyobjc_framework_systemconfiguration-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d12d5078611c905162bc951dffbb2a989b0dfd156952ba1884736c8dcbe38f7f", size = 21732, upload-time = "2025-06-14T20:55:43.951Z" },
{ url = "https://files.pythonhosted.org/packages/b9/61/0e9841bf1c7597f380a6dcefcc9335b6a909f20d9bdf07910cddc8552b42/pyobjc_framework_systemconfiguration-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6881929b828a566bf1349f09db4943e96a2b33f42556e1f7f6f28b192420f6fc", size = 21639, upload-time = "2025-06-14T20:55:44.678Z" },
{ url = "https://files.pythonhosted.org/packages/1c/eb/4480a1ab5baba4b9e75bb7f4f667073db5702cf521ddc99941575167585d/pyobjc_framework_systemconfiguration-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ab2ff52e4228f42182b7ef398d0da504f9f8f4a889963422af9aa1f495668db2", size = 21646, upload-time = "2025-06-14T20:55:45.426Z" },
@@ -3302,7 +3144,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/b4/57/4609fd9183383616b1e643c2489ad774335f679523a974b9ce346a6d4d5b/pyobjc_framework_systemextensions-11.1.tar.gz", hash = "sha256:8ff9f0aad14dcdd07dd47545c1dd20df7a286306967b0a0232c81fcc382babe6", size = 23062, upload-time = "2025-06-14T20:58:40.686Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/70/23/221c3b0e08b3bfbc779b80da53dd09aba9599ef5b42eac78262469c18195/pyobjc_framework_systemextensions-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:55e33ce532c16e36e0960e34501748d07d019f8088aa4efde10c5c91ccbce5aa", size = 9121, upload-time = "2025-06-14T20:55:49.623Z" },
{ url = "https://files.pythonhosted.org/packages/10/53/0fb6a200383fa98001ffa66b4f6344c68ccd092506699a353b30f18d7094/pyobjc_framework_systemextensions-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7e742ae51cdd86c0e609fe47189ea446de98d13b235b0a138a3f2e37e98cd359", size = 9125, upload-time = "2025-06-14T20:55:50.431Z" },
{ url = "https://files.pythonhosted.org/packages/76/40/d9be444b39ec12d68b5e4f712b71d6c00d654936ff5744ea380c1bfabf06/pyobjc_framework_systemextensions-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3a2b1e84e4a118bfe13efb9f2888b065dc937e2a7e60afd4d0a82b51b8301a10", size = 9130, upload-time = "2025-06-14T20:55:51.127Z" },
{ url = "https://files.pythonhosted.org/packages/7d/23/f615d69b3a86e75af234149fc12c8dfde8f346148e4eb185696a9c87e824/pyobjc_framework_systemextensions-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2ed65857244f18b88107e5d3ea8ea21c9da662490895b430e376423ee7c0b963", size = 9154, upload-time = "2025-06-14T20:55:51.798Z" },
@@ -3347,7 +3188,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/b4/4c/e7e180fcd06c246c37f218bcb01c40ea0213fde5ace3c09d359e60dcaafd/pyobjc_framework_usernotifications-11.1.tar.gz", hash = "sha256:38fc763afa7854b41ddfca8803f679a7305d278af8a7ad02044adc1265699996", size = 55428, upload-time = "2025-06-14T20:58:42.572Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6e/ee/27dc87a7fc1b8f55f97a01eb480f0d9db73a6cab64da91a8c5a609f8dced/pyobjc_framework_usernotifications-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:863f9c680ce9d4b0d398a61803210e4c7ff770487b6506f00742dd45cd4d4347", size = 9601, upload-time = "2025-06-14T20:55:57.238Z" },
{ url = "https://files.pythonhosted.org/packages/c1/bb/ae9c9301a86b7c0c26583c59ac761374cb6928c3d34cae514939e93e44b1/pyobjc_framework_usernotifications-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7140d337dd9dc3635add2177086429fdd6ef24970935b22fffdc5ec7f02ebf60", size = 9599, upload-time = "2025-06-14T20:55:58.051Z" },
{ url = "https://files.pythonhosted.org/packages/03/af/a54e343a7226dc65a65f7a561c060f8c96cb9f92f41ce2242d20d82ae594/pyobjc_framework_usernotifications-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ce6006989fd4a59ec355f6797ccdc9946014ea5241ff7875854799934dbba901", size = 9606, upload-time = "2025-06-14T20:55:59.088Z" },
{ url = "https://files.pythonhosted.org/packages/d1/fb/ae1ea7f7c511714c1502fa9c4856c6b3dfe110ff7cc094070fec5ad496b8/pyobjc_framework_usernotifications-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9efa3004059a8fe3f3c52f638f0401dbcdbc7b2f539587c8868da2486a64d674", size = 9628, upload-time = "2025-06-14T20:55:59.807Z" },
@@ -3395,7 +3235,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/e5/e3/df9096f54ae1f27cab8f922ee70cbda5d80f8c1d12734c38580829858133/pyobjc_framework_videotoolbox-11.1.tar.gz", hash = "sha256:a27985656e1b639cdb102fcc727ebc39f71bb1a44cdb751c8c80cc9fe938f3a9", size = 88551, upload-time = "2025-06-14T20:58:44.566Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/24/2c/497d9cd5b3763e85d06e6c84fbd15e0e72a3bc0e3e4103575bdc7c3546b0/pyobjc_framework_videotoolbox-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dad01cdc1fe2b5ca4ba4f2472eb62fca87898e1a4ade3b692177bb09a07d4254", size = 17326, upload-time = "2025-06-14T20:56:06.807Z" },
{ url = "https://files.pythonhosted.org/packages/0b/41/fda951f1c734a68d7bf46ecc03bfff376a690ad771029c4289ba0423a52e/pyobjc_framework_videotoolbox-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:94c17bffe0f4692db2e7641390dfdcd0f73ddbb0afa6c81ef504219be0777930", size = 17325, upload-time = "2025-06-14T20:56:07.719Z" },
{ url = "https://files.pythonhosted.org/packages/1f/cf/569babadbf1f9598f62c400ee02da19d4ab5f36276978c81080999399df9/pyobjc_framework_videotoolbox-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c55285c3c78183fd2a092d582e30b562777a82985cccca9e7e99a0aff2601591", size = 17432, upload-time = "2025-06-14T20:56:08.457Z" },
{ url = "https://files.pythonhosted.org/packages/b1/32/1a3d1a448d3cbcaf5c2a4ceaaad32817df21739099e187bbe6e3fd03d6fd/pyobjc_framework_videotoolbox-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:65a96385e80cb9ad3eab7d1f3156452ff805a925c9ca287ff1491a97cca191ba", size = 17450, upload-time = "2025-06-14T20:56:09.239Z" },
@@ -3414,7 +3253,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/f1/ff/57214e8f42755eeaad516a7e673dae4341b8742005d368ecc22c7a790b0b/pyobjc_framework_virtualization-11.1.tar.gz", hash = "sha256:4221ee5eb669e43a2ff46e04178bec149af2d65205deb5d4db5fa62ea060e022", size = 78633, upload-time = "2025-06-14T20:58:45.358Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6a/79/fd0310f6dffa56ae801c5829abf30e7c410340abcb3aad81595e1f43d330/pyobjc_framework_virtualization-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:27b3149426ab80583d8b40a0c0829d0968621b2c406abeeee1ac7ba3f25f9949", size = 13048, upload-time = "2025-06-14T20:56:13.021Z" },
{ url = "https://files.pythonhosted.org/packages/64/8b/5eeabfd08d5e6801010496969c1b67517bbda348ff0578ca5f075aa58926/pyobjc_framework_virtualization-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c2a812da4c995e1f8076678130d0b0a63042aa48219f8fb43b70e13eabcbdbc2", size = 13054, upload-time = "2025-06-14T20:56:13.866Z" },
{ url = "https://files.pythonhosted.org/packages/c8/4f/fe1930f4ce2c7d2f4c34bb53adf43f412bc91364e8e4cb450a7c8a6b8b59/pyobjc_framework_virtualization-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:59df6702b3e63200752be7d9c0dc590cb4c3b699c886f9a8634dd224c74b3c3c", size = 13084, upload-time = "2025-06-14T20:56:14.617Z" },
{ url = "https://files.pythonhosted.org/packages/4f/33/6d9f4177983d8894d217b212c25cbb91004cb1103c865961f03360aff68b/pyobjc_framework_virtualization-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:12a5ef32d2b7a56b675ea34fcb68bb9dddb7cf2c0a5ac5131f35551767bdacf1", size = 13093, upload-time = "2025-06-14T20:56:15.322Z" },
@@ -3435,7 +3273,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/40/a8/7128da4d0a0103cabe58910a7233e2f98d18c590b1d36d4b3efaaedba6b9/pyobjc_framework_vision-11.1.tar.gz", hash = "sha256:26590512ee7758da3056499062a344b8a351b178be66d4b719327884dde4216b", size = 133721, upload-time = "2025-06-14T20:58:46.095Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7d/e5/e98f3fd2b66e83451d4631b8f0b56d098474b73b91940216f376fb9d74c8/pyobjc_framework_vision-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c6f46df632096f070e16ba902a483fcb95c01fe12856a071bc2b25ac4a89bf3", size = 21652, upload-time = "2025-06-14T20:56:19.371Z" },
{ url = "https://files.pythonhosted.org/packages/10/69/a745a5491d7af6034ac9e0d627e7b41b42978df0a469b86cdf372ba8917f/pyobjc_framework_vision-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bfbde43c9d4296e1d26548b6d30ae413e2029425968cd8bce96d3c5a735e8f2c", size = 21657, upload-time = "2025-06-14T20:56:20.265Z" },
{ url = "https://files.pythonhosted.org/packages/a2/b5/54c0227a695557ea3065bc035b20a5c256f6f3b861e095eee1ec4b4d8cee/pyobjc_framework_vision-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df076c3e3e672887182953efc934c1f9683304737e792ec09a29bfee90d2e26a", size = 16829, upload-time = "2025-06-14T20:56:21.355Z" },
{ url = "https://files.pythonhosted.org/packages/20/cf/58ace43525ab073b39df9a740e855ebe83ed78f041d619644af3c60d9013/pyobjc_framework_vision-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1e5617e37dd2a7cff5e69e9aab039ea74b39ccdc528f6c828f2b60c1254e61e5", size = 16852, upload-time = "2025-06-14T20:56:22.081Z" },
@@ -3454,7 +3291,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/92/04/fb3d0b68994f7e657ef00c1ac5fc1c04ae2fc7ea581d647f5ae1f6739b14/pyobjc_framework_webkit-11.1.tar.gz", hash = "sha256:27e701c7aaf4f24fc7e601a128e2ef14f2773f4ab071b9db7438dc5afb5053ae", size = 717102, upload-time = "2025-06-14T20:58:47.461Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8c/f4/6c9f4cb3ec64ceb1d3176199e0b07fad5ab629ca7cceaddd193ba6200728/pyobjc_framework_webkit-11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5e7c254ba37b7a41fe9ffd31565495cad961a82ab22727949cdb4aface7f3fa6", size = 51406, upload-time = "2025-06-14T20:56:25.838Z" },
{ url = "https://files.pythonhosted.org/packages/8d/b6/d62c01a83c22619edf2379a6941c9f6b7aee01c565b9c1170696f85cba95/pyobjc_framework_webkit-11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:10ec89d727af8f216ba5911ff5553f84a5b660f5ddf75b07788e3a439c281165", size = 51406, upload-time = "2025-06-14T20:56:26.845Z" },
{ url = "https://files.pythonhosted.org/packages/8a/7e/fa2c18c0c0f9321e5036e54b9da7a196956b531e50fe1a76e7dfdbe8fac2/pyobjc_framework_webkit-11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1a6e6f64ca53c4953f17e808ecac11da288d9a6ade738156ba161732a5e0c96a", size = 51464, upload-time = "2025-06-14T20:56:27.653Z" },
{ url = "https://files.pythonhosted.org/packages/7a/8d/66561d95b00b8e57a9d5725ae34a8d9ca7ebeb776f13add989421ff90279/pyobjc_framework_webkit-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1d01008756c3912b02b7c02f62432467fbee90a93e3b8e31fa351b4ca97c9c98", size = 51495, upload-time = "2025-06-14T20:56:28.464Z" },
@@ -3503,12 +3339,10 @@ version = "8.4.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "iniconfig" },
{ name = "packaging" },
{ name = "pluggy" },
{ name = "pygments" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" }
wheels = [
@@ -3533,9 +3367,6 @@ name = "pywin32"
version = "311"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" },
{ url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" },
{ url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" },
{ url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" },
{ url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" },
{ url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" },
@@ -3565,15 +3396,6 @@ version = "6.0.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" },
{ url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" },
{ url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" },
{ url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" },
{ url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" },
{ url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" },
{ url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" },
{ url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" },
{ url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" },
{ url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" },
{ url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" },
{ url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" },
@@ -3609,21 +3431,6 @@ version = "3.13.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ed/f6/6895abc3a3d056b9698da3199b04c0e56226d530ae44a470edabf8b664f0/rapidfuzz-3.13.0.tar.gz", hash = "sha256:d2eaf3839e52cbcc0accbe9817a67b4b0fcf70aaeb229cfddc1c28061f9ce5d8", size = 57904226, upload-time = "2025-04-03T20:38:51.226Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/de/27/ca10b3166024ae19a7e7c21f73c58dfd4b7fef7420e5497ee64ce6b73453/rapidfuzz-3.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aafc42a1dc5e1beeba52cd83baa41372228d6d8266f6d803c16dbabbcc156255", size = 1998899, upload-time = "2025-04-03T20:35:08.764Z" },
{ url = "https://files.pythonhosted.org/packages/f0/38/c4c404b13af0315483a6909b3a29636e18e1359307fb74a333fdccb3730d/rapidfuzz-3.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:85c9a131a44a95f9cac2eb6e65531db014e09d89c4f18c7b1fa54979cb9ff1f3", size = 1449949, upload-time = "2025-04-03T20:35:11.26Z" },
{ url = "https://files.pythonhosted.org/packages/12/ae/15c71d68a6df6b8e24595421fdf5bcb305888318e870b7be8d935a9187ee/rapidfuzz-3.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d7cec4242d30dd521ef91c0df872e14449d1dffc2a6990ede33943b0dae56c3", size = 1424199, upload-time = "2025-04-03T20:35:12.954Z" },
{ url = "https://files.pythonhosted.org/packages/dc/9a/765beb9e14d7b30d12e2d6019e8b93747a0bedbc1d0cce13184fa3825426/rapidfuzz-3.13.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e297c09972698c95649e89121e3550cee761ca3640cd005e24aaa2619175464e", size = 5352400, upload-time = "2025-04-03T20:35:15.421Z" },
{ url = "https://files.pythonhosted.org/packages/e2/b8/49479fe6f06b06cd54d6345ed16de3d1ac659b57730bdbe897df1e059471/rapidfuzz-3.13.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef0f5f03f61b0e5a57b1df7beafd83df993fd5811a09871bad6038d08e526d0d", size = 1652465, upload-time = "2025-04-03T20:35:18.43Z" },
{ url = "https://files.pythonhosted.org/packages/6f/d8/08823d496b7dd142a7b5d2da04337df6673a14677cfdb72f2604c64ead69/rapidfuzz-3.13.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8cf5f7cd6e4d5eb272baf6a54e182b2c237548d048e2882258336533f3f02b7", size = 1616590, upload-time = "2025-04-03T20:35:20.482Z" },
{ url = "https://files.pythonhosted.org/packages/38/d4/5cfbc9a997e544f07f301c54d42aac9e0d28d457d543169e4ec859b8ce0d/rapidfuzz-3.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9256218ac8f1a957806ec2fb9a6ddfc6c32ea937c0429e88cf16362a20ed8602", size = 3086956, upload-time = "2025-04-03T20:35:22.756Z" },
{ url = "https://files.pythonhosted.org/packages/25/1e/06d8932a72fa9576095234a15785136407acf8f9a7dbc8136389a3429da1/rapidfuzz-3.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1bdd2e6d0c5f9706ef7595773a81ca2b40f3b33fd7f9840b726fb00c6c4eb2e", size = 2494220, upload-time = "2025-04-03T20:35:25.563Z" },
{ url = "https://files.pythonhosted.org/packages/03/16/5acf15df63119d5ca3d9a54b82807866ff403461811d077201ca351a40c3/rapidfuzz-3.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5280be8fd7e2bee5822e254fe0a5763aa0ad57054b85a32a3d9970e9b09bbcbf", size = 7585481, upload-time = "2025-04-03T20:35:27.426Z" },
{ url = "https://files.pythonhosted.org/packages/e1/cf/ebade4009431ea8e715e59e882477a970834ddaacd1a670095705b86bd0d/rapidfuzz-3.13.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd742c03885db1fce798a1cd87a20f47f144ccf26d75d52feb6f2bae3d57af05", size = 2894842, upload-time = "2025-04-03T20:35:29.457Z" },
{ url = "https://files.pythonhosted.org/packages/a7/bd/0732632bd3f906bf613229ee1b7cbfba77515db714a0e307becfa8a970ae/rapidfuzz-3.13.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5435fcac94c9ecf0504bf88a8a60c55482c32e18e108d6079a0089c47f3f8cf6", size = 3438517, upload-time = "2025-04-03T20:35:31.381Z" },
{ url = "https://files.pythonhosted.org/packages/83/89/d3bd47ec9f4b0890f62aea143a1e35f78f3d8329b93d9495b4fa8a3cbfc3/rapidfuzz-3.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:93a755266856599be4ab6346273f192acde3102d7aa0735e2f48b456397a041f", size = 4412773, upload-time = "2025-04-03T20:35:33.425Z" },
{ url = "https://files.pythonhosted.org/packages/b3/57/1a152a07883e672fc117c7f553f5b933f6e43c431ac3fd0e8dae5008f481/rapidfuzz-3.13.0-cp310-cp310-win32.whl", hash = "sha256:3abe6a4e8eb4cfc4cda04dd650a2dc6d2934cbdeda5def7e6fd1c20f6e7d2a0b", size = 1842334, upload-time = "2025-04-03T20:35:35.648Z" },
{ url = "https://files.pythonhosted.org/packages/a7/68/7248addf95b6ca51fc9d955161072285da3059dd1472b0de773cff910963/rapidfuzz-3.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:e8ddb58961401da7d6f55f185512c0d6bd24f529a637078d41dd8ffa5a49c107", size = 1624392, upload-time = "2025-04-03T20:35:37.294Z" },
{ url = "https://files.pythonhosted.org/packages/68/23/f41c749f2c61ed1ed5575eaf9e73ef9406bfedbf20a3ffa438d15b5bf87e/rapidfuzz-3.13.0-cp310-cp310-win_arm64.whl", hash = "sha256:c523620d14ebd03a8d473c89e05fa1ae152821920c3ff78b839218ff69e19ca3", size = 865584, upload-time = "2025-04-03T20:35:39.005Z" },
{ url = "https://files.pythonhosted.org/packages/87/17/9be9eff5a3c7dfc831c2511262082c6786dca2ce21aa8194eef1cb71d67a/rapidfuzz-3.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d395a5cad0c09c7f096433e5fd4224d83b53298d53499945a9b0e5a971a84f3a", size = 1999453, upload-time = "2025-04-03T20:35:40.804Z" },
{ url = "https://files.pythonhosted.org/packages/75/67/62e57896ecbabe363f027d24cc769d55dd49019e576533ec10e492fcd8a2/rapidfuzz-3.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7b3eda607a019169f7187328a8d1648fb9a90265087f6903d7ee3a8eee01805", size = 1450881, upload-time = "2025-04-03T20:35:42.734Z" },
{ url = "https://files.pythonhosted.org/packages/96/5c/691c5304857f3476a7b3df99e91efc32428cbe7d25d234e967cc08346c13/rapidfuzz-3.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98e0bfa602e1942d542de077baf15d658bd9d5dcfe9b762aff791724c1c38b70", size = 1422990, upload-time = "2025-04-03T20:35:45.158Z" },
@@ -3669,12 +3476,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/f3/5e0c6ae452cbb74e5436d3445467447e8c32f3021f48f93f15934b8cffc2/rapidfuzz-3.13.0-cp313-cp313-win32.whl", hash = "sha256:0e1d08cb884805a543f2de1f6744069495ef527e279e05370dd7c83416af83f8", size = 1822066, upload-time = "2025-04-03T20:37:14.425Z" },
{ url = "https://files.pythonhosted.org/packages/96/e3/a98c25c4f74051df4dcf2f393176b8663bfd93c7afc6692c84e96de147a2/rapidfuzz-3.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9a7c6232be5f809cd39da30ee5d24e6cadd919831e6020ec6c2391f4c3bc9264", size = 1615100, upload-time = "2025-04-03T20:37:16.611Z" },
{ url = "https://files.pythonhosted.org/packages/60/b1/05cd5e697c00cd46d7791915f571b38c8531f714832eff2c5e34537c49ee/rapidfuzz-3.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:3f32f15bacd1838c929b35c84b43618481e1b3d7a61b5ed2db0291b70ae88b53", size = 858976, upload-time = "2025-04-03T20:37:19.336Z" },
{ url = "https://files.pythonhosted.org/packages/d5/e1/f5d85ae3c53df6f817ca70dbdd37c83f31e64caced5bb867bec6b43d1fdf/rapidfuzz-3.13.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fe5790a36d33a5d0a6a1f802aa42ecae282bf29ac6f7506d8e12510847b82a45", size = 1904437, upload-time = "2025-04-03T20:38:00.255Z" },
{ url = "https://files.pythonhosted.org/packages/db/d7/ded50603dddc5eb182b7ce547a523ab67b3bf42b89736f93a230a398a445/rapidfuzz-3.13.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:cdb33ee9f8a8e4742c6b268fa6bd739024f34651a06b26913381b1413ebe7590", size = 1383126, upload-time = "2025-04-03T20:38:02.676Z" },
{ url = "https://files.pythonhosted.org/packages/c4/48/6f795e793babb0120b63a165496d64f989b9438efbeed3357d9a226ce575/rapidfuzz-3.13.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c99b76b93f7b495eee7dcb0d6a38fb3ce91e72e99d9f78faa5664a881cb2b7d", size = 1365565, upload-time = "2025-04-03T20:38:06.646Z" },
{ url = "https://files.pythonhosted.org/packages/f0/50/0062a959a2d72ed17815824e40e2eefdb26f6c51d627389514510a7875f3/rapidfuzz-3.13.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6af42f2ede8b596a6aaf6d49fdee3066ca578f4856b85ab5c1e2145de367a12d", size = 5251719, upload-time = "2025-04-03T20:38:09.191Z" },
{ url = "https://files.pythonhosted.org/packages/e7/02/bd8b70cd98b7a88e1621264778ac830c9daa7745cd63e838bd773b1aeebd/rapidfuzz-3.13.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c0efa73afbc5b265aca0d8a467ae2a3f40d6854cbe1481cb442a62b7bf23c99", size = 2991095, upload-time = "2025-04-03T20:38:12.554Z" },
{ url = "https://files.pythonhosted.org/packages/9f/8d/632d895cdae8356826184864d74a5f487d40cb79f50a9137510524a1ba86/rapidfuzz-3.13.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7ac21489de962a4e2fc1e8f0b0da4aa1adc6ab9512fd845563fecb4b4c52093a", size = 1553888, upload-time = "2025-04-03T20:38:15.357Z" },
{ url = "https://files.pythonhosted.org/packages/88/df/6060c5a9c879b302bd47a73fc012d0db37abf6544c57591bcbc3459673bd/rapidfuzz-3.13.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1ba007f4d35a45ee68656b2eb83b8715e11d0f90e5b9f02d615a8a321ff00c27", size = 1905935, upload-time = "2025-04-03T20:38:18.07Z" },
{ url = "https://files.pythonhosted.org/packages/a2/6c/a0b819b829e20525ef1bd58fc776fb8d07a0c38d819e63ba2b7c311a2ed4/rapidfuzz-3.13.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d7a217310429b43be95b3b8ad7f8fc41aba341109dc91e978cd7c703f928c58f", size = 1383714, upload-time = "2025-04-03T20:38:20.628Z" },
{ url = "https://files.pythonhosted.org/packages/6a/c1/3da3466cc8a9bfb9cd345ad221fac311143b6a9664b5af4adb95b5e6ce01/rapidfuzz-3.13.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:558bf526bcd777de32b7885790a95a9548ffdcce68f704a81207be4a286c1095", size = 1367329, upload-time = "2025-04-03T20:38:23.01Z" },
@@ -3753,42 +3554,12 @@ wheels = [
]
[[package]]
name = "tomli"
version = "2.2.1"
name = "tomli-w"
version = "1.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" }
sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" },
{ url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" },
{ url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" },
{ url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" },
{ url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" },
{ url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" },
{ url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" },
{ url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" },
{ url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" },
{ url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" },
{ url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" },
{ url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" },
{ url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" },
{ url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" },
{ url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" },
{ url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" },
{ url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" },
{ url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" },
{ url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" },
{ url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" },
{ url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" },
{ url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" },
{ url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" },
{ url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" },
{ url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" },
{ url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" },
{ url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" },
{ url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" },
{ url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" },
{ url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" },
{ url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" },
{ url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" },
]
[[package]]
@@ -3820,7 +3591,6 @@ dependencies = [
{ name = "distlib" },
{ name = "filelock" },
{ name = "platformdirs" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1c/14/37fcdba2808a6c615681cd216fecae00413c9dab44fb2e57805ecf3eaee3/virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a", size = 6003808, upload-time = "2025-08-13T14:24:07.464Z" }
wheels = [
@@ -3837,6 +3607,7 @@ dependencies = [
{ name = "inquirerpy" },
{ name = "pydantic" },
{ name = "rich" },
{ name = "tomli-w" },
]
[package.optional-dependencies]
@@ -3906,6 +3677,7 @@ requires-dist = [
{ name = "pypresence", marker = "extra == 'discord'", specifier = ">=4.3.0" },
{ name = "rich", specifier = ">=13.9.2" },
{ name = "thefuzz", marker = "extra == 'standard'", specifier = ">=0.22.1" },
{ name = "tomli-w", specifier = ">=1.0.0" },
{ name = "yt-dlp", marker = "extra == 'download'", specifier = ">=2025.7.21" },
{ name = "yt-dlp", marker = "extra == 'standard'", specifier = ">=2025.7.21" },
]

View File

@@ -1,6 +1,6 @@
import sys
if sys.version_info < (3, 10):
if sys.version_info < (3, 11):
raise ImportError(
"You are using an unsupported version of Python. Only Python versions 3.10 and above are supported by Viu"
) # noqa: F541

View File

@@ -1,4 +1,3 @@
██╗░░░██╗██╗██╗░░░██╗
██║░░░██║██║██║░░░██║
╚██╗░██╔╝██║██║░░░██║

View File

@@ -6,7 +6,7 @@ import click
from click.core import ParameterSource
from ..core.config import AppConfig
from ..core.constants import PROJECT_NAME, USER_CONFIG, __version__
from ..core.constants import CLI_NAME, USER_CONFIG, __version__
from .config import ConfigLoader
from .options import options_from_model
from .utils.exception import setup_exceptions_handler
@@ -39,6 +39,7 @@ commands = {
"worker": "worker.worker",
"queue": "queue.queue",
"completions": "completions.completions",
"plugin": "plugin.plugin",
}
@@ -47,7 +48,7 @@ commands = {
root="viu_media.cli.commands",
invoke_without_command=True,
lazy_subcommands=commands,
context_settings=dict(auto_envvar_prefix=PROJECT_NAME),
context_settings=dict(auto_envvar_prefix=CLI_NAME),
)
@click.version_option(__version__, "--version")
@click.option("--no-config", is_flag=True, help="Don't load the user config file.")
@@ -108,6 +109,49 @@ def cli(ctx: click.Context, **options: "Unpack[Options]"):
else loader.load(cli_overrides)
)
ctx.obj = config
if config.general.check_for_updates:
import time
from ..core.constants import APP_CACHE_DIR
last_updated_at_file = APP_CACHE_DIR / "last_update"
should_check_for_update = False
if last_updated_at_file.exists():
try:
last_updated_at_time = float(
last_updated_at_file.read_text(encoding="utf-8")
)
if (
time.time() - last_updated_at_time
) > config.general.update_check_interval * 3600:
should_check_for_update = True
except Exception as e:
logger.warning(f"Failed to check for update: {e}")
else:
should_check_for_update = True
if should_check_for_update:
last_updated_at_file.write_text(str(time.time()), encoding="utf-8")
from .service.feedback import FeedbackService
from .utils.update import check_for_updates, print_release_json, update_app
feedback = FeedbackService(config)
feedback.info("Checking for updates...")
is_latest, release_json = check_for_updates()
if not is_latest:
from ..libs.selectors.selector import create_selector
selector = create_selector(config)
if release_json and selector.confirm(
"Theres an update available would you like to see the release notes before deciding to update?"
):
print_release_json(release_json)
selector.ask("Enter to continue...")
if selector.confirm("Would you like to update?"):
update_app()
if ctx.invoked_subcommand is None:
from .commands.anilist import cmd

View File

@@ -72,7 +72,7 @@ def config(
):
from ...core.constants import USER_CONFIG
from ..config.editor import InteractiveConfigEditor
from ..config.generate import generate_config_ini_from_app_model
from ..config.generate import generate_config_toml_from_app_model
if path:
print(USER_CONFIG)
@@ -81,9 +81,9 @@ def config(
from rich.syntax import Syntax
console = Console()
config_ini = generate_config_ini_from_app_model(user_config)
config_toml = generate_config_toml_from_app_model(user_config)
syntax = Syntax(
config_ini,
config_toml,
"ini",
theme=user_config.general.pygment_style,
line_numbers=True,
@@ -99,12 +99,14 @@ def config(
elif interactive:
editor = InteractiveConfigEditor(current_config=user_config)
new_config = editor.run()
with open(USER_CONFIG, "w", encoding="utf-8") as file:
file.write(generate_config_ini_from_app_model(new_config))
USER_CONFIG.write_text(
generate_config_toml_from_app_model(new_config), encoding="utf-8"
)
click.echo(f"Configuration saved successfully to {USER_CONFIG}")
elif update:
with open(USER_CONFIG, "w", encoding="utf-8") as file:
file.write(generate_config_ini_from_app_model(user_config))
USER_CONFIG.write_text(
generate_config_toml_from_app_model(user_config), encoding="utf-8"
)
print("update successfull")
else:
click.edit(filename=str(USER_CONFIG))
@@ -123,9 +125,9 @@ def _generate_desktop_entry():
from rich.prompt import Confirm
from ...core.constants import (
CLI_NAME,
ICON_PATH,
PLATFORM,
PROJECT_NAME,
USER_APPLICATIONS,
__version__,
)
@@ -149,7 +151,7 @@ def _generate_desktop_entry():
desktop_entry = dedent(
f"""
[Desktop Entry]
Name={PROJECT_NAME.title()}
Name={CLI_NAME.title()}
Type=Application
version={__version__}
Path={Path().home()}
@@ -160,7 +162,7 @@ def _generate_desktop_entry():
Categories=Entertainment
"""
)
desktop_entry_path = USER_APPLICATIONS / f"{PROJECT_NAME}.desktop"
desktop_entry_path = USER_APPLICATIONS / f"{CLI_NAME}.desktop"
if desktop_entry_path.exists():
if not Confirm.ask(
f"The file already exists {desktop_entry_path}; or would you like to rewrite it",

View File

@@ -0,0 +1,5 @@
"""Plugin management commands for viu."""
from .cmd import plugin
__all__ = ["plugin"]

View File

@@ -0,0 +1,24 @@
"""Main plugin command group."""
import click
from ...utils.lazyloader import LazyGroup
lazy_subcommands = {
"add": "add.add",
"remove": "remove.remove",
"list": "list_plugins.list_plugins",
"update": "update.update",
}
@click.group(
name="plugin",
cls=LazyGroup,
root="viu_media.cli.commands.plugin.commands",
lazy_subcommands=lazy_subcommands,
help="Manage viu plugins (providers, players, selectors, commands)"
)
def plugin() -> None:
"""Manage viu plugins."""
pass

View File

@@ -0,0 +1 @@
"""Plugin command implementations."""

View File

@@ -0,0 +1,54 @@
"""Add plugin command."""
import click
from rich.console import Console
from typing import cast
from viu_media.core.plugins.manager import PluginError, plugin_manager, ComponentType
console = Console()
@click.command()
@click.option(
"--type",
"plugin_type",
type=click.Choice(["provider", "player", "selector", "command"]),
required=True,
help="Type of plugin to install"
)
@click.option(
"--force",
is_flag=True,
help="Force installation, overwriting existing plugin"
)
@click.argument("name")
@click.argument("source")
def add(plugin_type: str, name: str, source: str, force: bool) -> None:
"""Install a plugin from a Git repository.
NAME: Local name for the plugin
SOURCE: Git source (e.g., 'github:user/repo' or full URL)
Examples:
viu plugin add --type provider gogoanime github:user/viu-gogoanime
viu plugin add --type player custom-mpv https://github.com/user/viu-mpv-plugin
"""
try:
console.print(f"Installing {plugin_type} plugin '{name}' from {source}...")
plugin_manager.add_plugin(cast(ComponentType, plugin_type), name, source, force=force)
console.print(f"✅ Successfully installed plugin '{name}'", style="green")
# Show configuration hint
from viu_media.core.constants import PLUGINS_CONFIG
console.print(
f"\n💡 Configure the plugin by editing: {PLUGINS_CONFIG}",
style="blue"
)
except PluginError as e:
console.print(f"❌ Failed to install plugin: {e}", style="red")
raise click.ClickException(str(e))
except Exception as e:
console.print(f"❌ Unexpected error: {e}", style="red")
raise click.ClickException(f"Unexpected error: {e}")

View File

@@ -0,0 +1,74 @@
"""List plugins command."""
import click
from rich.console import Console
from rich.table import Table
from typing import cast
from viu_media.core.plugins.manager import plugin_manager, ComponentType
console = Console()
@click.command(name="list")
@click.option(
"--type",
"plugin_type",
type=click.Choice(["provider", "player", "selector", "command"]),
help="Filter by plugin type"
)
def list_plugins(plugin_type: str) -> None:
"""List installed plugins.
Examples:
viu plugin list
viu plugin list --type provider
"""
all_plugins = plugin_manager.list_plugins()
# Filter by type if specified
if plugin_type:
plugins_to_show = {cast(ComponentType, plugin_type): all_plugins[cast(ComponentType, plugin_type)]}
else:
plugins_to_show = all_plugins
# Count total plugins
total_count = sum(len(plugins) for plugins in plugins_to_show.values())
if total_count == 0:
if plugin_type:
console.print(f"No {plugin_type} plugins installed.", style="yellow")
else:
console.print("No plugins installed.", style="yellow")
console.print("Install plugins with: viu plugin add --type <type> <name> <source>")
return
# Create table
table = Table(title="Installed Plugins")
table.add_column("Type", style="cyan")
table.add_column("Name", style="green")
table.add_column("Version", style="yellow")
table.add_column("Source", style="blue")
table.add_column("Path", style="magenta")
# Add rows
for component_type, plugins in plugins_to_show.items():
for name, plugin_info in plugins.items():
table.add_row(
component_type,
name,
plugin_info.version or "unknown",
plugin_info.source,
str(plugin_info.path)
)
console.print(table)
console.print(f"\nTotal: {total_count} plugin(s)")
# Show configuration hint if plugins exist
if total_count > 0:
from viu_media.core.constants import PLUGINS_CONFIG
console.print(
f"\n💡 Configure plugins by editing: {PLUGINS_CONFIG}",
style="blue"
)

View File

@@ -0,0 +1,43 @@
"""Remove plugin command."""
import click
from rich.console import Console
from typing import cast
from viu_media.core.plugins.manager import PluginError, PluginNotFoundError, plugin_manager, ComponentType
console = Console()
@click.command()
@click.option(
"--type",
"plugin_type",
type=click.Choice(["provider", "player", "selector", "command"]),
required=True,
help="Type of plugin to remove"
)
@click.argument("name")
def remove(plugin_type: str, name: str) -> None:
"""Remove an installed plugin.
NAME: Name of the plugin to remove
Examples:
viu plugin remove --type provider gogoanime
viu plugin remove --type player custom-mpv
"""
try:
console.print(f"Removing {plugin_type} plugin '{name}'...")
plugin_manager.remove_plugin(cast(ComponentType, plugin_type), name)
console.print(f"✅ Successfully removed plugin '{name}'", style="green")
except PluginNotFoundError as e:
console.print(f"❌ Plugin not found: {e}", style="red")
raise click.ClickException(str(e))
except PluginError as e:
console.print(f"❌ Failed to remove plugin: {e}", style="red")
raise click.ClickException(str(e))
except Exception as e:
console.print(f"❌ Unexpected error: {e}", style="red")
raise click.ClickException(f"Unexpected error: {e}")

View File

@@ -0,0 +1,43 @@
"""Update plugin command."""
import click
from rich.console import Console
from typing import cast
from viu_media.core.plugins.manager import PluginError, PluginNotFoundError, plugin_manager, ComponentType
console = Console()
@click.command()
@click.option(
"--type",
"plugin_type",
type=click.Choice(["provider", "player", "selector", "command"]),
required=True,
help="Type of plugin to update"
)
@click.argument("name")
def update(plugin_type: str, name: str) -> None:
"""Update an installed plugin by pulling from Git.
NAME: Name of the plugin to update
Examples:
viu plugin update --type provider gogoanime
viu plugin update --type player custom-mpv
"""
try:
console.print(f"Updating {plugin_type} plugin '{name}'...")
plugin_manager.update_plugin(cast(ComponentType, plugin_type), name)
console.print(f"✅ Successfully updated plugin '{name}'", style="green")
except PluginNotFoundError as e:
console.print(f"❌ Plugin not found: {e}", style="red")
raise click.ClickException(str(e))
except PluginError as e:
console.print(f"❌ Failed to update plugin: {e}", style="red")
raise click.ClickException(str(e))
except Exception as e:
console.print(f"❌ Unexpected error: {e}", style="red")
raise click.ClickException(f"Unexpected error: {e}")

View File

@@ -5,10 +5,8 @@ from typing import TYPE_CHECKING
import click
from rich import print
from rich.console import Console
from rich.markdown import Markdown
from ..utils.update import check_for_updates, update_app
from ..utils.update import check_for_updates, print_release_json, update_app
if TYPE_CHECKING:
from ...core.config import AppConfig
@@ -74,87 +72,46 @@ def update(
check_only: Whether to only check for updates without updating
release_notes: Whether to show release notes for the latest version
"""
try:
if release_notes:
print("[cyan]Fetching latest release notes...[/]")
is_latest, release_json = check_for_updates()
if release_notes:
print("[cyan]Fetching latest release notes...[/]")
is_latest, release_json = check_for_updates()
if not release_json:
print(
"[yellow]Could not fetch release information. Please check your internet connection.[/]"
)
sys.exit(1)
version = release_json.get("tag_name", "unknown")
release_name = release_json.get("name", version)
release_body = release_json.get("body", "No release notes available.")
published_at = release_json.get("published_at", "unknown")
console = Console()
print(f"[bold cyan]Release: {release_name}[/]")
print(f"[dim]Version: {version}[/]")
print(f"[dim]Published: {published_at}[/]")
print()
# Display release notes as markdown if available
if release_body.strip():
markdown = Markdown(release_body)
console.print(markdown)
else:
print("[dim]No release notes available for this version.[/]")
return
elif check_only:
print("[cyan]Checking for updates...[/]")
is_latest, release_json = check_for_updates()
if not release_json:
print(
"[yellow]Could not check for updates. Please check your internet connection.[/]"
)
sys.exit(1)
if is_latest:
print("[green]Viu is up to date![/]")
print(
f"[dim]Current version: {release_json.get('tag_name', 'unknown')}[/]"
)
else:
latest_version = release_json.get("tag_name", "unknown")
print(f"[yellow]Update available: {latest_version}[/]")
print("[dim]Run 'viu update' to update[/]")
sys.exit(1)
if not release_json:
print(
"[yellow]Could not fetch release information. Please check your internet connection.[/]"
)
else:
print("[cyan]Checking for updates and updating if necessary...[/]")
success, release_json = update_app(force=force)
print_release_json(release_json)
if not release_json:
print(
"[red]Could not check for updates. Please check your internet connection.[/]"
)
sys.exit(1)
return
if success:
latest_version = release_json.get("tag_name", "unknown")
print(f"[green]Successfully updated to version {latest_version}![/]")
else:
if force:
print(
"[red]Update failed. Please check the error messages above.[/]"
)
sys.exit(1)
# If not forced and update failed, it might be because already up to date
# The update_app function already prints appropriate messages
elif check_only:
print("[cyan]Checking for updates...[/]")
is_latest, release_json = check_for_updates()
except KeyboardInterrupt:
print("\n[yellow]Update cancelled by user.[/]")
sys.exit(1)
except Exception as e:
print(f"[red]An error occurred during update: {e}[/]")
# Get trace option from parent context
trace = ctx.parent.params.get("trace", False) if ctx.parent else False
if trace:
raise
sys.exit(1)
if not release_json:
print(
"[yellow]Could not check for updates. Please check your internet connection.[/]"
)
if is_latest:
print("[green]Viu is up to date![/]")
print(f"[dim]Current version: {release_json.get('tag_name', 'unknown')}[/]")
else:
latest_version = release_json.get("tag_name", "unknown")
print(f"[yellow]Update available: {latest_version}[/]")
print("[dim]Run 'viu update' to update[/]")
else:
print("[cyan]Checking for updates and updating if necessary...[/]")
success, release_json = update_app(force=force)
if not release_json:
print(
"[red]Could not check for updates. Please check your internet connection.[/]"
)
if success:
latest_version = release_json.get("tag_name", "unknown")
print(f"[green]Successfully updated to version {latest_version}![/]")
else:
if force:
print("[red]Update failed. Please check the error messages above.[/]")

View File

@@ -1,4 +1,4 @@
from .generate import generate_config_ini_from_app_model
from .generate import generate_config_toml_from_app_model
from .loader import ConfigLoader
__all__ = ["ConfigLoader", "generate_config_ini_from_app_model"]
__all__ = ["ConfigLoader", "generate_config_toml_from_app_model"]

View File

@@ -1,4 +1,6 @@
# viu_media/cli/config/generate.py
import itertools
import json
import textwrap
from enum import Enum
from pathlib import Path
@@ -8,7 +10,7 @@ from pydantic.fields import ComputedFieldInfo, FieldInfo
from pydantic_core import PydanticUndefined
from ...core.config import AppConfig
from ...core.constants import APP_ASCII_ART, DISCORD_INVITE, PROJECT_NAME, REPO_HOME
from ...core.constants import APP_ASCII_ART, CLI_NAME, DISCORD_INVITE, REPO_HOME
# The header for the config file.
config_asci = "\n".join(
@@ -20,15 +22,17 @@ CONFIG_HEADER = f"""
{config_asci}
#
# ==============================================================================
# This file was auto-generated from the application's configuration model.
# This is the Viu configuration file. It uses the TOML format.
# You can modify these values to customize the behavior of Viu.
# For path-based options, you can use '~' for your home directory.
# For more information on the available options, please refer to the
# official documentation on GitHub.
# ==============================================================================
""".lstrip()
CONFIG_FOOTER = f"""
# ==============================================================================
#
# HOPE YOU ENJOY {PROJECT_NAME} AND BE SURE TO STAR THE PROJECT ON GITHUB
# HOPE YOU ENJOY {CLI_NAME} AND BE SURE TO STAR THE PROJECT ON GITHUB
# {REPO_HOME}
#
# Also join the discord server
@@ -39,21 +43,22 @@ CONFIG_FOOTER = f"""
""".lstrip()
def generate_config_ini_from_app_model(app_model: AppConfig) -> str:
"""Generate a configuration file content from a Pydantic model."""
def generate_config_toml_from_app_model(app_model: AppConfig) -> str:
"""Generate a TOML configuration file content from a Pydantic model with comments."""
config_ini_content = [CONFIG_HEADER]
config_content_parts = [CONFIG_HEADER]
for section_name, section_model in app_model:
section_comment = section_model.model_config.get("title", "")
section_title = section_model.model_config.get("title", section_name.title())
config_ini_content.append(f"\n#\n# {section_comment}\n#")
config_ini_content.append(f"[{section_name}]")
config_content_parts.append(f"\n#\n# {section_title}\n#")
config_content_parts.append(f"[{section_name}]")
for field_name, field_info in itertools.chain(
section_model.model_fields.items(),
section_model.model_computed_fields.items(),
):
# --- Generate Comments ---
description = field_info.description or ""
if description:
wrapped_comment = textwrap.fill(
@@ -62,7 +67,7 @@ def generate_config_ini_from_app_model(app_model: AppConfig) -> str:
initial_indent="# ",
subsequent_indent="# ",
)
config_ini_content.append(f"\n{wrapped_comment}")
config_content_parts.append(f"\n{wrapped_comment}")
field_type_comment = _get_field_type_comment(field_info)
if field_type_comment:
@@ -72,35 +77,65 @@ def generate_config_ini_from_app_model(app_model: AppConfig) -> str:
initial_indent="# ",
subsequent_indent="# ",
)
config_ini_content.append(wrapped_comment)
config_content_parts.append(wrapped_comment)
if (
hasattr(field_info, "default")
and field_info.default != PydanticUndefined
and field_info.default is not PydanticUndefined
):
default_val = (
field_info.default.value
if isinstance(field_info.default, Enum)
else field_info.default
)
wrapped_comment = textwrap.fill(
f"Default: {field_info.default.value if isinstance(field_info.default, Enum) else field_info.default}",
f"Default: {_format_toml_value(default_val)}",
width=78,
initial_indent="# ",
subsequent_indent="# ",
)
config_ini_content.append(wrapped_comment)
config_content_parts.append(wrapped_comment)
# --- Generate Key-Value Pair ---
field_value = getattr(section_model, field_name)
if isinstance(field_value, bool):
value_str = str(field_value).lower()
elif isinstance(field_value, Path):
value_str = str(field_value)
elif field_value is None:
value_str = ""
elif isinstance(field_value, Enum):
value_str = field_value.value
if field_value is None:
config_content_parts.append(f"# {field_name} =")
else:
value_str = str(field_value)
value_str = _format_toml_value(field_value)
config_content_parts.append(f"{field_name} = {value_str}")
config_ini_content.append(f"{field_name} = {value_str}")
config_content_parts.extend(["\n", CONFIG_FOOTER])
return "\n".join(config_content_parts)
config_ini_content.extend(["\n", CONFIG_FOOTER])
return "\n".join(config_ini_content)
def _format_toml_value(value: Any) -> str:
"""
Manually formats a Python value into a TOML-compliant string.
This avoids needing an external TOML writer dependency.
"""
if isinstance(value, bool):
return str(value).lower()
if isinstance(value, (int, float)):
return str(value)
if isinstance(value, Enum):
return f'"{value.value}"'
# Handle strings and Paths, differentiating between single and multi-line
if isinstance(value, (str, Path)):
str_val = str(value)
if "\n" in str_val:
# For multi-line strings, use triple quotes.
# Also, escape any triple quotes that might be in the string itself.
escaped_val = str_val.replace('"""', '\\"\\"\\"')
return f'"""\n{escaped_val}"""'
else:
# For single-line strings, use double quotes and escape relevant characters.
escaped_val = str_val.replace("\\", "\\\\").replace('"', '\\"')
return f'"{escaped_val}"'
# Fallback for any other types
return f'"{str(value)}"'
def _get_field_type_comment(field_info: FieldInfo | ComputedFieldInfo) -> str:
@@ -111,7 +146,6 @@ def _get_field_type_comment(field_info: FieldInfo | ComputedFieldInfo) -> str:
else field_info.return_type
)
# Handle Literal and Enum types
possible_values = []
if field_type is not None:
if isinstance(field_type, type) and issubclass(field_type, Enum):
@@ -122,9 +156,8 @@ def _get_field_type_comment(field_info: FieldInfo | ComputedFieldInfo) -> str:
possible_values = list(args)
if possible_values:
return f"Possible values: [ {', '.join(map(str, possible_values))} ]"
# Handle basic types and numeric ranges
formatted_values = ", ".join(json.dumps(v) for v in possible_values)
return f"Possible values: [ {formatted_values} ]"
type_name = _get_type_name(field_type)
range_info = _get_range_info(field_info)

View File

@@ -1,4 +1,5 @@
import configparser
import logging
import tomllib
from pathlib import Path
from typing import Dict
@@ -9,12 +10,14 @@ from ...core.config import AppConfig
from ...core.constants import USER_CONFIG
from ...core.exceptions import ConfigError
logger = logging.getLogger(__name__)
class ConfigLoader:
"""
Handles loading the application configuration from an .ini file.
Handles loading the application configuration from a .toml file.
It ensures a default configuration exists, reads the .ini file,
It ensures a default configuration exists, reads the .toml file,
and uses Pydantic to parse and validate the data into a type-safe
AppConfig object.
"""
@@ -24,26 +27,19 @@ class ConfigLoader:
Initializes the loader with the path to the configuration file.
Args:
config_path: The path to the user's config.ini file.
config_path: The path to the user's config.toml file.
"""
self.config_path = config_path
self.parser = configparser.ConfigParser(
interpolation=None,
# Allow boolean values without a corresponding value (e.g., `enabled` vs `enabled = true`)
allow_no_value=True,
# Behave like a dictionary, preserving case sensitivity of keys
dict_type=dict,
)
def _handle_first_run(self) -> AppConfig:
"""Handles the configuration process when no config file is found."""
"""Handles the configuration process when no config.toml file is found."""
click.echo(
"[bold yellow]Welcome to Viu![/bold yellow] No configuration file found."
)
from InquirerPy import inquirer
from .editor import InteractiveConfigEditor
from .generate import generate_config_ini_from_app_model
from .generate import generate_config_toml_from_app_model
choice = inquirer.select( # type: ignore
message="How would you like to proceed?",
@@ -60,16 +56,17 @@ class ConfigLoader:
else:
app_config = AppConfig()
config_ini_content = generate_config_ini_from_app_model(app_config)
config_toml_content = generate_config_toml_from_app_model(app_config)
try:
self.config_path.parent.mkdir(parents=True, exist_ok=True)
self.config_path.write_text(config_ini_content, encoding="utf-8")
self.config_path.write_text(config_toml_content, encoding="utf-8")
click.echo(
f"Configuration file created at: [green]{self.config_path}[/green]"
)
except Exception as e:
raise ConfigError(
f"Could not create configuration file at {self.config_path!s}. Please check permissions. Error: {e}",
f"Could not create configuration file at {self.config_path!s}. "
f"Please check permissions. Error: {e}",
)
return app_config
@@ -78,32 +75,34 @@ class ConfigLoader:
"""
Loads the configuration and returns a populated, validated AppConfig object.
Args:
update: A dictionary of CLI overrides to apply to the loaded config.
Returns:
An instance of AppConfig with values from the user's .ini file.
An instance of AppConfig with values from the user's .toml file.
Raises:
click.ClickException: If the configuration file contains validation errors.
ConfigError: If the configuration file contains validation or parsing errors.
"""
if not self.config_path.exists():
return self._handle_first_run()
try:
self.parser.read(self.config_path, encoding="utf-8")
except configparser.Error as e:
with self.config_path.open("rb") as f:
config_dict = tomllib.load(f)
except tomllib.TOMLDecodeError as e:
raise ConfigError(
f"Error parsing configuration file '{self.config_path}':\n{e}"
)
# Convert the configparser object into a nested dictionary that mirrors
# the structure of our AppConfig Pydantic model.
config_dict = {
section: dict(self.parser.items(section))
for section in self.parser.sections()
}
# Apply CLI overrides on top of the loaded configuration
if update:
for key in config_dict:
if key in update:
config_dict[key].update(update[key])
for section, values in update.items():
if section in config_dict:
config_dict[section].update(values)
else:
config_dict[section] = values
try:
app_config = AppConfig.model_validate(config_dict)
return app_config

View File

@@ -66,13 +66,13 @@ class MediaApiState(StateModel):
@property
def search_result(self) -> dict[int, MediaItem]:
if not self.search_result_:
if self.search_result_ is None:
raise RuntimeError("Malformed state, please report")
return self.search_result_
@property
def search_params(self) -> Union[MediaSearchParams, UserMediaListSearchParams]:
if not self.search_params_:
if self.search_params_ is None:
raise RuntimeError("Malformed state, please report")
return self.search_params_
@@ -84,7 +84,7 @@ class MediaApiState(StateModel):
@property
def media_id(self) -> int:
if not self.media_id_:
if self.media_id_ is None:
raise RuntimeError("Malformed state, please report")
return self.media_id_
@@ -105,13 +105,13 @@ class ProviderState(StateModel):
@property
def search_results(self) -> SearchResults:
if not self.search_results_:
if self.search_results_ is None:
raise RuntimeError("Malformed state, please report")
return self.search_results_
@property
def anime(self) -> Anime:
if not self.anime_:
if self.anime_ is None:
raise RuntimeError("Malformed state, please report")
return self.anime_
@@ -123,13 +123,13 @@ class ProviderState(StateModel):
@property
def servers(self) -> Dict[str, Server]:
if not self.servers_:
if self.servers_ is None:
raise RuntimeError("Malformed state, please report")
return self.servers_
@property
def server_name(self) -> str:
if not self.server_name_:
if self.server_name_ is None:
raise RuntimeError("Malformed state, please report")
return self.server_name_

View File

@@ -34,12 +34,12 @@ class FeedbackService:
try:
from plyer import notification
from ....core.constants import ICON_PATH, PROJECT_NAME
from ....core.constants import CLI_NAME, ICON_PATH
notification.notify( # type: ignore
title=f"{PROJECT_NAME} notification".title(),
title=f"{CLI_NAME} notification".title(),
message=message,
app_name=PROJECT_NAME,
app_name=CLI_NAME,
app_icon=str(ICON_PATH),
timeout=self.app_config.general.desktop_notification_duration * 60,
)
@@ -60,12 +60,12 @@ class FeedbackService:
try:
from plyer import notification
from ....core.constants import ICON_PATH, PROJECT_NAME
from ....core.constants import CLI_NAME, ICON_PATH
notification.notify( # type: ignore
title=f"{PROJECT_NAME} notification".title(),
title=f"{CLI_NAME} notification".title(),
message=message,
app_name=PROJECT_NAME,
app_name=CLI_NAME,
app_icon=str(ICON_PATH),
timeout=self.app_config.general.desktop_notification_duration * 60,
)
@@ -87,12 +87,12 @@ class FeedbackService:
try:
from plyer import notification
from ....core.constants import ICON_PATH, PROJECT_NAME
from ....core.constants import CLI_NAME, ICON_PATH
notification.notify( # type: ignore
title=f"{PROJECT_NAME} notification".title(),
title=f"{CLI_NAME} notification".title(),
message=message,
app_name=PROJECT_NAME,
app_name=CLI_NAME,
app_icon=str(ICON_PATH),
timeout=self.app_config.general.desktop_notification_duration * 60,
)
@@ -113,12 +113,12 @@ class FeedbackService:
try:
from plyer import notification
from ....core.constants import ICON_PATH, PROJECT_NAME
from ....core.constants import CLI_NAME, ICON_PATH
notification.notify( # type: ignore
title=f"{PROJECT_NAME} notification".title(),
title=f"{CLI_NAME} notification".title(),
message=message,
app_name=PROJECT_NAME,
app_name=CLI_NAME,
app_icon=str(ICON_PATH),
timeout=self.app_config.general.desktop_notification_duration * 60,
)
@@ -169,12 +169,12 @@ class FeedbackService:
try:
from plyer import notification
from ....core.constants import ICON_PATH, PROJECT_NAME
from ....core.constants import CLI_NAME, ICON_PATH
notification.notify( # type: ignore
title=f"{PROJECT_NAME} notification".title(),
title=f"{CLI_NAME} notification".title(),
message="No current way to display info in rofi, use fzf and the terminal instead",
app_name=PROJECT_NAME,
app_name=CLI_NAME,
app_icon=str(ICON_PATH),
timeout=self.app_config.general.desktop_notification_duration * 60,
)

View File

@@ -8,14 +8,41 @@ import sys
from httpx import get
from rich import print
from rich.console import Console
from rich.markdown import Markdown
from ...core.constants import AUTHOR, GIT_REPO, PROJECT_NAME_LOWER, __version__
from ...core.constants import (
AUTHOR,
CLI_NAME_LOWER,
GIT_REPO,
PROJECT_NAME,
__version__,
)
API_URL = f"https://api.{GIT_REPO}/repos/{AUTHOR}/{PROJECT_NAME_LOWER}/releases/latest"
API_URL = f"https://api.{GIT_REPO}/repos/{AUTHOR}/{CLI_NAME_LOWER}/releases/latest"
def print_release_json(release_json):
version = release_json.get("tag_name", "unknown")
release_name = release_json.get("name", version)
release_body = release_json.get("body", "No release notes available.")
published_at = release_json.get("published_at", "unknown")
console = Console()
print(f"[bold cyan]Release: {release_name}[/]")
print(f"[dim]Version: {version}[/]")
print(f"[dim]Published: {published_at}[/]")
print()
# Display release notes as markdown if available
if release_body and release_body.strip():
markdown = Markdown(release_body)
console.print(markdown)
def check_for_updates():
USER_AGENT = f"{PROJECT_NAME_LOWER} user"
USER_AGENT = f"{CLI_NAME_LOWER} user"
try:
response = get(
API_URL,
@@ -96,9 +123,9 @@ def update_app(force=False):
return False, release_json
process = subprocess.run(
[NIX, "profile", "upgrade", PROJECT_NAME_LOWER], check=False
[NIX, "profile", "upgrade", CLI_NAME_LOWER], check=False
)
elif is_git_repo(AUTHOR, PROJECT_NAME_LOWER):
elif is_git_repo(AUTHOR, CLI_NAME_LOWER):
GIT_EXECUTABLE = shutil.which("git")
args = [
GIT_EXECUTABLE,
@@ -117,11 +144,9 @@ def update_app(force=False):
)
elif UV := shutil.which("uv"):
process = subprocess.run(
[UV, "tool", "upgrade", PROJECT_NAME_LOWER], check=False
)
process = subprocess.run([UV, "tool", "upgrade", PROJECT_NAME], check=False)
elif PIPX := shutil.which("pipx"):
process = subprocess.run([PIPX, "upgrade", PROJECT_NAME_LOWER], check=False)
process = subprocess.run([PIPX, "upgrade", PROJECT_NAME], check=False)
else:
PYTHON_EXECUTABLE = sys.executable
@@ -130,7 +155,7 @@ def update_app(force=False):
"-m",
"pip",
"install",
PROJECT_NAME_LOWER,
PROJECT_NAME,
"-U",
"--no-warn-script-location",
]

View File

@@ -32,6 +32,7 @@ def GENERAL_IMAGE_RENDERER():
GENERAL_MANGA_VIEWER = "feh"
GENERAL_CHECK_FOR_UPDATES = True
GENERAL_UPDATE_CHECK_INTERVAL = 12
GENERAL_CACHE_REQUESTS = True
GENERAL_MAX_CACHE_LIFETIME = "03:00:00"
GENERAL_NORMALIZE_TITLES = True

View File

@@ -24,6 +24,7 @@ GENERAL_IMAGE_RENDERER = (
)
GENERAL_MANGA_VIEWER = "The external application to use for viewing manga pages."
GENERAL_CHECK_FOR_UPDATES = "Automatically check for new versions of Viu on startup."
GENERAL_UPDATE_CHECK_INTERVAL = "The interval in hours to check for updates"
GENERAL_CACHE_REQUESTS = (
"Enable caching of network requests to speed up subsequent operations."
)

View File

@@ -1,7 +1,7 @@
from pathlib import Path
from typing import Literal
from pydantic import BaseModel, Field, PrivateAttr, computed_field
from pydantic import BaseModel, Field
from ...libs.media_api.types import MediaSort, UserMediaListSort
from ...libs.provider.anime.types import ProviderName, ProviderServer
@@ -190,6 +190,10 @@ class GeneralConfig(BaseModel):
default=defaults.GENERAL_CHECK_FOR_UPDATES,
description=desc.GENERAL_CHECK_FOR_UPDATES,
)
update_check_interval: float = Field(
default=defaults.GENERAL_UPDATE_CHECK_INTERVAL,
description=desc.GENERAL_UPDATE_CHECK_INTERVAL,
)
cache_requests: bool = Field(
default=defaults.GENERAL_CACHE_REQUESTS,
description=desc.GENERAL_CACHE_REQUESTS,
@@ -319,14 +323,16 @@ class SessionsConfig(OtherConfig):
class FzfConfig(OtherConfig):
"""Configuration specific to the FZF selector."""
_opts: str = PrivateAttr(
default_factory=lambda: defaults.FZF_OPTS.read_text(encoding="utf-8")
opts: str = Field(
default_factory=lambda: defaults.FZF_OPTS.read_text(encoding="utf-8"),
description=desc.FZF_OPTS,
)
header_color: str = Field(
default=defaults.FZF_HEADER_COLOR, description=desc.FZF_HEADER_COLOR
)
_header_ascii_art: str = PrivateAttr(
default_factory=lambda: APP_ASCII_ART.read_text(encoding="utf-8")
header_ascii_art: str = Field(
default_factory=lambda: APP_ASCII_ART.read_text(encoding="utf-8"),
description=desc.FZF_HEADER_ASCII_ART,
)
preview_header_color: str = Field(
default=defaults.FZF_PREVIEW_HEADER_COLOR,
@@ -337,28 +343,6 @@ class FzfConfig(OtherConfig):
description=desc.FZF_PREVIEW_SEPARATOR_COLOR,
)
def __init__(self, **kwargs):
opts = kwargs.pop("opts", None)
header_ascii_art = kwargs.pop("header_ascii_art", None)
super().__init__(**kwargs)
if opts:
self._opts = opts
if header_ascii_art:
self._header_ascii_art = header_ascii_art
@computed_field(description=desc.FZF_OPTS)
@property
def opts(self) -> str:
return "\n" + "\n".join([f"\t{line}" for line in self._opts.split()])
@computed_field(description=desc.FZF_HEADER_ASCII_ART)
@property
def header_ascii_art(self) -> str:
return "\n" + "\n".join(
[f"\t{line}" for line in self._header_ascii_art.split()]
)
class RofiConfig(OtherConfig):
"""Configuration specific to the Rofi selector."""

View File

@@ -4,9 +4,10 @@ from importlib import metadata, resources
from pathlib import Path
PLATFORM = sys.platform
PROJECT_NAME = "VIU"
PROJECT_NAME_LOWER = "viu"
APP_NAME = os.environ.get(f"{PROJECT_NAME}_APP_NAME", PROJECT_NAME_LOWER)
CLI_NAME = "VIU"
CLI_NAME_LOWER = "viu"
PROJECT_NAME = "viu-media"
APP_NAME = os.environ.get(f"{CLI_NAME}_APP_NAME", CLI_NAME_LOWER)
USER_NAME = os.environ.get("USERNAME", "User")
@@ -24,7 +25,7 @@ ANILIST_AUTH = (
)
try:
APP_DIR = Path(str(resources.files(PROJECT_NAME.lower())))
APP_DIR = Path(str(resources.files(CLI_NAME.lower())))
except ModuleNotFoundError:
from pathlib import Path
@@ -75,12 +76,18 @@ else:
USER_APPLICATIONS = Path.home() / ".local" / "share" / "applications"
LOG_FOLDER = APP_CACHE_DIR / "logs"
# Plugin system paths
PLUGINS_DIR = APP_DATA_DIR / "plugins"
PLUGINS_MANIFEST = APP_DATA_DIR / "plugins.toml"
PLUGINS_CONFIG = APP_DATA_DIR / "plugins.config.toml"
# USER_APPLICATIONS.mkdir(parents=True,exist_ok=True)
APP_DATA_DIR.mkdir(parents=True, exist_ok=True)
APP_CACHE_DIR.mkdir(parents=True, exist_ok=True)
LOG_FOLDER.mkdir(parents=True, exist_ok=True)
USER_VIDEOS_DIR.mkdir(parents=True, exist_ok=True)
PLUGINS_DIR.mkdir(parents=True, exist_ok=True)
USER_CONFIG = APP_DATA_DIR / "config.ini"
USER_CONFIG = APP_DATA_DIR / "config.toml"
LOG_FILE = LOG_FOLDER / "app.log"

View File

@@ -0,0 +1,6 @@
"""Plugin system for viu."""
from .model import PluginComponents, PluginInfo
from .manager import PluginManager
__all__ = ["PluginInfo", "PluginComponents", "PluginManager"]

View File

@@ -0,0 +1,646 @@
"""Plugin manager for viu.
This module contains the PluginManager singleton that handles all plugin operations
including loading, discovery, installation, and removal.
"""
import importlib.util
import logging
import shutil
import subprocess
import sys
import tomllib
from pathlib import Path
from typing import Any, Dict, Literal, Optional, Set, Union
import tomli_w
from pydantic import ValidationError
from viu_media.core.exceptions import ViuError
from ..constants import PLUGINS_CONFIG, PLUGINS_DIR, PLUGINS_MANIFEST
from .model import InstalledPlugin, PluginInfo, PluginManifest
logger = logging.getLogger(__name__)
ComponentType = Literal["provider", "player", "selector", "command"]
class PluginError(ViuError):
"""Base exception for plugin-related errors."""
pass
class PluginNotFoundError(ViuError):
"""Raised when a requested plugin is not found."""
pass
class PluginLoadError(ViuError):
"""Raised when a plugin fails to load."""
pass
class PluginManager:
"""Manages the plugin system for viu.
This is a singleton class that handles:
- Loading and caching plugins
- Installing and removing plugins from Git repositories
- Managing plugin configurations
- Discovering available plugins
"""
_instance: Optional["PluginManager"] = None
_initialized: bool = False
def __new__(cls) -> "PluginManager":
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self) -> None:
if self._initialized:
return
self._loaded_components: Dict[str, Any] = {}
self._manifest: PluginManifest = PluginManifest()
self._plugin_configs: Dict[str, Dict[str, Any]] = {}
self._load_manifest()
self._load_plugin_configs()
self._initialized = True
def load_component(self, component_type: ComponentType, name: str) -> Any:
"""Lazy-load a plugin component by type and name.
Args:
component_type: Type of component (provider, player, selector, command)
name: Name of the component to load
Returns:
The loaded component instance or function
Raises:
PluginNotFoundError: If the plugin is not installed
PluginLoadError: If the plugin fails to load
"""
cache_key = f"{component_type}:{name}"
# Return cached component if already loaded
if cache_key in self._loaded_components:
return self._loaded_components[cache_key]
# Find the plugin in the manifest
plugins_of_type = getattr(self._manifest, f"{component_type}s")
if name not in plugins_of_type:
raise PluginNotFoundError(
f"Plugin '{name}' of type '{component_type}' is not installed"
)
plugin_entry = plugins_of_type[name]
plugin_path = plugin_entry.path
if not plugin_path.exists():
raise PluginLoadError(f"Plugin path does not exist: {plugin_path}")
# Load plugin info to get component definition
try:
plugin_info = self._get_plugin_info(plugin_path)
except PluginError as e:
raise PluginLoadError(f"Failed to load plugin info: {e}") from e
# Get the component definition
component_def = getattr(plugin_info.components, component_type)
if not component_def:
raise PluginLoadError(
f"Plugin '{name}' does not provide a {component_type} component"
)
# Parse module:class format
if ":" not in component_def:
raise PluginLoadError(f"Invalid component definition: {component_def}")
module_name, class_name = component_def.split(":", 1)
# Load the module
module_path = plugin_path / f"{module_name}.py"
if not module_path.exists():
raise PluginLoadError(f"Plugin module not found: {module_path}")
try:
spec = importlib.util.spec_from_file_location(
f"plugin_{name}_{module_name}", module_path
)
if spec is None or spec.loader is None:
raise PluginLoadError(f"Could not create module spec for {module_path}")
module = importlib.util.module_from_spec(spec)
# Add plugin path to sys.path temporarily for relative imports
sys.path.insert(0, str(plugin_path))
try:
spec.loader.exec_module(module)
finally:
sys.path.remove(str(plugin_path))
except Exception as e:
raise PluginLoadError(f"Failed to load module {module_path}: {e}") from e
# Get the component class/function
if not hasattr(module, class_name):
raise PluginLoadError(f"Module {module_name} does not have {class_name}")
component_cls = getattr(module, class_name)
# For providers, players, and selectors, instantiate with config
if component_type in ("provider", "player", "selector"):
plugin_config = self._plugin_configs.get(name, {})
# For providers, also inject httpx client like the built-in system
if component_type == "provider":
from httpx import Client
from ...core.utils.networking import random_user_agent
headers = getattr(component_cls, "HEADERS", {})
client = Client(headers={"User-Agent": random_user_agent(), **headers})
try:
component = component_cls(client, **plugin_config)
except TypeError:
# Fallback if constructor doesn't accept config
component = component_cls(client)
else:
try:
component = component_cls(**plugin_config)
except TypeError:
# Fallback if constructor doesn't accept config
component = component_cls()
else:
# For commands, just return the function
component = component_cls
# Cache and return
self._loaded_components[cache_key] = component
logger.debug(f"Loaded plugin component: {cache_key}")
return component
def add_plugin(
self, component_type: ComponentType, name: str, source: str, force: bool = False
) -> None:
"""Install a plugin from a Git repository.
Args:
component_type: Type of component the plugin provides
name: Local name for the plugin
source: Git source (e.g., "github:user/repo")
force: Whether to overwrite existing plugin
Raises:
PluginError: If installation fails
"""
plugins_of_type = getattr(self._manifest, f"{component_type}s")
# Check if plugin already exists
if name in plugins_of_type and not force:
raise PluginError(
f"Plugin '{name}' already exists. Use --force to overwrite."
)
# Determine installation path
plugin_dir = PLUGINS_DIR / f"{component_type}s" / name
# Remove existing if force is True
if plugin_dir.exists():
if force:
shutil.rmtree(plugin_dir)
else:
raise PluginError(f"Plugin directory already exists: {plugin_dir}")
# Create parent directory
plugin_dir.parent.mkdir(parents=True, exist_ok=True)
# Clone the repository
self._clone_plugin(source, plugin_dir)
# Validate plugin structure
try:
plugin_info = self._get_plugin_info(plugin_dir)
except PluginError:
# Clean up on validation failure
shutil.rmtree(plugin_dir)
raise
# Ensure plugin provides the expected component type
expected_component = getattr(plugin_info.components, component_type)
if not expected_component:
shutil.rmtree(plugin_dir)
raise PluginError(f"Plugin does not provide a {component_type} component")
# Add to manifest
plugins_of_type[name] = InstalledPlugin(
source=source, path=plugin_dir, version=plugin_info.plugin.version
)
# Save manifest
self._save_manifest()
# Copy default config if it exists
self._install_default_config(name, plugin_dir)
logger.info(f"Successfully installed {component_type} plugin '{name}'")
def remove_plugin(self, component_type: ComponentType, name: str) -> None:
"""Remove an installed plugin.
Args:
component_type: Type of component
name: Name of the plugin to remove
Raises:
PluginNotFoundError: If plugin is not installed
PluginError: If removal fails
"""
plugins_of_type = getattr(self._manifest, f"{component_type}s")
if name not in plugins_of_type:
raise PluginNotFoundError(
f"Plugin '{name}' of type '{component_type}' is not installed"
)
plugin_entry = plugins_of_type[name]
plugin_path = plugin_entry.path
# Remove from filesystem
if plugin_path.exists():
try:
shutil.rmtree(plugin_path)
except OSError as e:
raise PluginError(f"Failed to remove plugin directory: {e}") from e
# Remove from manifest
del plugins_of_type[name]
# Remove from loaded components cache
cache_key = f"{component_type}:{name}"
self._loaded_components.pop(cache_key, None)
# Save manifest
self._save_manifest()
logger.info(f"Successfully removed {component_type} plugin '{name}'")
def update_plugin(self, component_type: ComponentType, name: str) -> None:
"""Update an installed plugin by pulling from Git.
Args:
component_type: Type of component
name: Name of the plugin to update
Raises:
PluginNotFoundError: If plugin is not installed
PluginError: If update fails
"""
plugins_of_type = getattr(self._manifest, f"{component_type}s")
if name not in plugins_of_type:
raise PluginNotFoundError(
f"Plugin '{name}' of type '{component_type}' is not installed"
)
plugin_entry = plugins_of_type[name]
plugin_path = plugin_entry.path
if not plugin_path.exists():
raise PluginError(f"Plugin path does not exist: {plugin_path}")
# Pull latest changes
try:
result = subprocess.run(
["git", "pull"],
cwd=plugin_path,
check=True,
capture_output=True,
text=True,
)
logger.debug(f"Git pull output: {result.stdout}")
except subprocess.CalledProcessError as e:
raise PluginError(f"Failed to update plugin: {e.stderr}") from e
except FileNotFoundError:
raise PluginError("Git is not installed or not in PATH") from None
# Update version in manifest
try:
plugin_info = self._get_plugin_info(plugin_path)
plugin_entry.version = plugin_info.plugin.version
self._save_manifest()
except PluginError as e:
logger.warning(f"Could not update plugin version: {e}")
# Clear from cache to force reload
cache_key = f"{component_type}:{name}"
self._loaded_components.pop(cache_key, None)
logger.info(f"Successfully updated {component_type} plugin '{name}'")
def list_plugins(self) -> Dict[ComponentType, Dict[str, InstalledPlugin]]:
"""List all installed plugins grouped by type.
Returns:
Dictionary mapping component types to their installed plugins
"""
return {
"provider": dict(self._manifest.providers),
"player": dict(self._manifest.players),
"selector": dict(self._manifest.selectors),
"command": dict(self._manifest.commands),
}
def get_available_components(self, component_type: ComponentType) -> Set[str]:
"""Get names of all available components of a given type.
This includes both built-in components and installed plugins.
Args:
component_type: Type of component
Returns:
Set of component names
"""
# Get plugin names
plugins_of_type = getattr(self._manifest, f"{component_type}s")
plugin_names = set(plugins_of_type.keys())
# Add built-in component names
if component_type == "provider":
from ...libs.provider.anime.provider import PROVIDERS_AVAILABLE
builtin_names = set(PROVIDERS_AVAILABLE.keys())
elif component_type == "player":
from ...libs.player.player import PLAYERS
builtin_names = set(PLAYERS)
elif component_type == "selector":
from ...libs.selectors.selector import SELECTORS
builtin_names = set(SELECTORS)
elif component_type == "command":
# Commands would need to be handled differently as they're registered in CLI
builtin_names = set()
else:
builtin_names = set()
return plugin_names | builtin_names
def is_plugin(self, component_type: ComponentType, name: str) -> bool:
"""Check if a component is provided by a plugin.
Args:
component_type: Type of component
name: Name of the component
Returns:
True if it's a plugin, False if it's built-in
"""
plugins_of_type = getattr(self._manifest, f"{component_type}s")
return name in plugins_of_type
def _load_manifest(self) -> None:
"""Load the plugins.toml manifest file."""
if not PLUGINS_MANIFEST.exists():
logger.debug("No plugins manifest found, creating empty one")
self._save_manifest()
return
try:
with open(PLUGINS_MANIFEST, "rb") as f:
data = tomllib.load(f)
self._manifest = PluginManifest.model_validate(data)
logger.debug(
f"Loaded plugins manifest with {len(self.list_plugins())} plugins"
)
except (OSError, ValidationError, tomllib.TOMLDecodeError) as e:
logger.error(f"Failed to load plugins manifest: {e}")
self._manifest = PluginManifest()
def _save_manifest(self) -> None:
"""Save the current manifest to plugins.toml."""
try:
# Convert Path objects to strings for TOML serialization
manifest_dict = self._manifest.model_dump()
# Convert all Path objects to strings
def convert_paths(obj: Any) -> Any:
if isinstance(obj, dict):
return {k: convert_paths(v) for k, v in obj.items()}
elif isinstance(obj, list):
return [convert_paths(item) for item in obj]
elif isinstance(obj, Path):
return str(obj)
else:
return obj
manifest_dict = convert_paths(manifest_dict)
with open(PLUGINS_MANIFEST, "wb") as f:
tomli_w.dump(manifest_dict, f)
logger.debug("Saved plugins manifest")
except OSError as e:
logger.error(f"Failed to save plugins manifest: {e}")
raise PluginError(f"Could not save plugins manifest: {e}") from e
def _load_plugin_configs(self) -> None:
"""Load plugin configurations from plugins.config.toml."""
if not PLUGINS_CONFIG.exists():
logger.debug("No plugin configs found")
return
try:
with open(PLUGINS_CONFIG, "rb") as f:
self._plugin_configs = tomllib.load(f)
logger.debug(f"Loaded configs for {len(self._plugin_configs)} plugins")
except (OSError, tomllib.TOMLDecodeError) as e:
logger.error(f"Failed to load plugin configs: {e}")
self._plugin_configs = {}
def _get_plugin_info(self, plugin_path: Path) -> PluginInfo:
"""Load and validate plugin.info.toml from a plugin directory."""
info_file = plugin_path / "plugin.info.toml"
if not info_file.exists():
raise PluginError(f"Plugin info file not found: {info_file}")
try:
with open(info_file, "rb") as f:
data = tomllib.load(f)
return PluginInfo.model_validate(data)
except (OSError, ValidationError, tomllib.TOMLDecodeError) as e:
raise PluginError(f"Invalid plugin info file {info_file}: {e}") from e
def _parse_git_source(self, source: str) -> tuple[str, str]:
"""Parse a git source string into platform and repo.
Examples:
"github:user/repo" -> ("github.com", "user/repo")
"gitlab:user/repo" -> ("gitlab.com", "user/repo")
"https://github.com/user/repo" -> ("github.com", "user/repo")
"/path/to/local/repo" -> ("local", "/path/to/local/repo")
"file:///path/to/repo" -> ("local", "/path/to/repo")
"""
# Handle local file paths
if source.startswith("file://"):
return "local", source[7:] # Remove file:// prefix
elif (
source.startswith("/")
or source.startswith("./")
or source.startswith("../")
):
return "local", source
if source.startswith("http"):
# Full URL provided
if "github.com" in source:
repo = source.split("github.com/")[-1].rstrip(".git")
return "github.com", repo
elif "gitlab.com" in source:
repo = source.split("gitlab.com/")[-1].rstrip(".git")
return "gitlab.com", repo
else:
raise PluginError(f"Unsupported git host in URL: {source}")
# Short format like "github:user/repo"
if ":" not in source:
raise PluginError(f"Invalid source format: {source}")
platform, repo = source.split(":", 1)
platform_map = {
"github": "github.com",
"gitlab": "gitlab.com",
}
if platform not in platform_map:
raise PluginError(f"Unsupported platform: {platform}")
return platform_map[platform], repo
def _clone_plugin(self, source: str, dest_path: Path) -> None:
"""Clone a plugin repository from Git."""
platform, repo = self._parse_git_source(source)
if platform == "local":
# Handle local repository - just copy the directory
import shutil
src_path = Path(repo).resolve()
if not src_path.exists():
raise PluginError(f"Local repository path does not exist: {src_path}")
if (src_path / ".git").exists():
logger.info(f"Copying local Git repository from {src_path}")
try:
# Use git clone to properly copy the repository
subprocess.run(
["git", "clone", str(src_path), str(dest_path)],
check=True,
capture_output=True,
text=True,
)
except subprocess.CalledProcessError as e:
raise PluginError(
f"Failed to clone local repository: {e.stderr}"
) from e
else:
logger.warning("Copying non git repo, local plugin")
shutil.copytree(src_path, dest_path)
else:
# Handle remote repository
git_url = f"https://{platform}/{repo}.git"
logger.info(f"Cloning plugin from {git_url}")
try:
subprocess.run(
["git", "clone", git_url, str(dest_path)],
check=True,
capture_output=True,
text=True,
)
except subprocess.CalledProcessError as e:
raise PluginError(f"Failed to clone plugin: {e.stderr}") from e
if not dest_path.exists():
raise PluginError(
"Plugin cloning failed - destination directory was not created"
)
# Check for git command availability
try:
subprocess.run(["git", "--version"], check=True, capture_output=True)
except FileNotFoundError:
raise PluginError("Git is not installed or not in PATH") from None
def _install_default_config(self, plugin_name: str, plugin_dir: Path) -> None:
"""Install default configuration from plugin's config.toml if it exists."""
default_config_path = plugin_dir / "config.toml"
if not default_config_path.exists():
logger.debug(f"No default config found for plugin '{plugin_name}'")
return
# Load the default config
try:
with open(default_config_path, "rb") as f:
default_config = tomllib.load(f)
except (OSError, tomllib.TOMLDecodeError) as e:
logger.warning(
f"Failed to load default config for plugin '{plugin_name}': {e}"
)
return
# Load existing plugins config or create empty dict
if PLUGINS_CONFIG.exists():
try:
with open(PLUGINS_CONFIG, "rb") as f:
existing_config = tomllib.load(f)
except (OSError, tomllib.TOMLDecodeError) as e:
logger.warning(f"Failed to load existing plugins config: {e}")
existing_config = {}
else:
existing_config = {}
# Check if plugin config already exists
if plugin_name in existing_config:
logger.debug(
f"Plugin '{plugin_name}' config already exists, skipping default config installation"
)
return
# Merge the default config
if plugin_name in default_config:
existing_config[plugin_name] = default_config[plugin_name]
# Write the updated config
try:
with open(PLUGINS_CONFIG, "wb") as f:
tomli_w.dump(existing_config, f)
logger.info(
f"Installed default configuration for plugin '{plugin_name}'"
)
except OSError as e:
logger.warning(
f"Failed to save default config for plugin '{plugin_name}': {e}"
)
else:
logger.debug(
f"No config section found for plugin '{plugin_name}' in default config"
)
# Global instance
plugin_manager = PluginManager()

View File

@@ -0,0 +1,91 @@
"""Plugin interface definitions for viu.
This module defines the Pydantic models that represent the structure
of plugin.info.toml files and plugin configurations.
"""
from pathlib import Path
from typing import Dict, Optional
from pydantic import BaseModel, Field
class PluginComponents(BaseModel):
"""Defines the components that a plugin provides.
Each component is defined as a string in the format:
{module_name_in_repo}:{ClassName_or_factory_function}
For example:
provider = "gogo_provider:GogoProvider"
player = "my_player:MyPlayer"
selector = "my_selector:MySelector"
command = "my_command:my_command_func"
"""
provider: Optional[str] = Field(
None,
description="Provider component in format 'module:class'"
)
player: Optional[str] = Field(
None,
description="Player component in format 'module:class'"
)
selector: Optional[str] = Field(
None,
description="Selector component in format 'module:class'"
)
command: Optional[str] = Field(
None,
description="Command component in format 'module:function'"
)
class PluginMetadata(BaseModel):
"""Plugin metadata from the [plugin] section."""
name: str = Field(description="Human-readable plugin name")
version: str = Field(description="Plugin version")
description: str = Field(description="Plugin description")
author: Optional[str] = Field(None, description="Plugin author")
homepage: Optional[str] = Field(None, description="Plugin homepage URL")
requires_python: Optional[str] = Field(
None,
description="Minimum Python version required"
)
class PluginInfo(BaseModel):
"""Complete plugin information from plugin.info.toml."""
plugin: PluginMetadata = Field(description="Plugin metadata")
components: PluginComponents = Field(description="Plugin components")
class InstalledPlugin(BaseModel):
"""Represents a plugin entry in plugins.toml."""
source: str = Field(description="Git source (e.g., 'github:user/repo')")
path: Path = Field(description="Local filesystem path to the plugin")
version: Optional[str] = Field(None, description="Installed version")
class PluginManifest(BaseModel):
"""Complete plugins.toml manifest structure."""
providers: Dict[str, InstalledPlugin] = Field(
default_factory=dict,
description="Installed provider plugins"
)
players: Dict[str, InstalledPlugin] = Field(
default_factory=dict,
description="Installed player plugins"
)
selectors: Dict[str, InstalledPlugin] = Field(
default_factory=dict,
description="Installed selector plugins"
)
commands: Dict[str, InstalledPlugin] = Field(
default_factory=dict,
description="Installed command plugins"
)

View File

@@ -30,8 +30,18 @@ class PlayerFactory:
ValueError: If the player_name is not supported.
NotImplementedError: If the player is recognized but not yet implemented.
"""
from ...core.plugins.manager import plugin_manager
player_name = config.stream.player
# Check if it's a plugin first
if plugin_manager.is_plugin("player", player_name):
try:
return plugin_manager.load_component("player", player_name)
except Exception as e:
raise ValueError(f"Could not load plugin player '{player_name}': {e}") from e
# Handle built-in players
if player_name not in PLAYERS:
raise ValueError(
f"Unsupported player: '{player_name}'. Supported players are: {PLAYERS}"

View File

@@ -1,5 +1,6 @@
import importlib
import logging
from typing import Union
from httpx import Client
@@ -21,12 +22,12 @@ class AnimeProviderFactory:
"""Factory for creating anime provider instances."""
@staticmethod
def create(provider_name: ProviderName) -> BaseAnimeProvider:
def create(provider_name: Union[ProviderName, str]) -> BaseAnimeProvider:
"""
Dynamically creates an instance of the specified anime provider.
This method imports the necessary provider module, instantiates its main class,
and injects a pre-configured HTTP client.
and injects a pre-configured HTTP client. It now also supports plugin providers.
Args:
provider_name: The name of the provider to create (e.g., 'allanime').
@@ -38,24 +39,43 @@ class AnimeProviderFactory:
ValueError: If the provider_name is not supported.
ImportError: If the provider module or class cannot be found.
"""
from ....core.plugins.manager import plugin_manager
from ....core.utils.networking import random_user_agent
# Convert to string if it's an enum
if isinstance(provider_name, ProviderName):
provider_str = provider_name.value
else:
provider_str = str(provider_name)
# Check if it's a plugin first
if plugin_manager.is_plugin("provider", provider_str):
try:
return plugin_manager.load_component("provider", provider_str)
except Exception as e:
logger.error(f"Failed to load plugin provider '{provider_str}': {e}")
raise ImportError(f"Could not load plugin provider '{provider_str}': {e}") from e
# Handle built-in providers
if provider_str.lower() not in PROVIDERS_AVAILABLE:
raise ValueError(f"Provider '{provider_str}' is not available")
# Correctly determine module and class name from the map
import_path = PROVIDERS_AVAILABLE[provider_name.value.lower()]
import_path = PROVIDERS_AVAILABLE[provider_str.lower()]
module_name, class_name = import_path.split(".", 1)
# Construct the full package path for dynamic import
package_path = f"viu_media.libs.provider.anime.{provider_name.value.lower()}"
package_path = f"viu_media.libs.provider.anime.{provider_str.lower()}"
try:
provider_module = importlib.import_module(f".{module_name}", package_path)
provider_class = getattr(provider_module, class_name)
except (ImportError, AttributeError) as e:
logger.error(
f"Failed to load provider '{provider_name.value.lower()}': {e}"
f"Failed to load provider '{provider_str}': {e}"
)
raise ImportError(
f"Could not load provider '{provider_name.value.lower()}'. "
f"Could not load provider '{provider_str}'. "
"Check the module path and class name in PROVIDERS_AVAILABLE."
) from e

View File

@@ -54,15 +54,15 @@ class RofiSelector(BaseSelector):
from plyer import notification
from ....core.constants import (
CLI_NAME,
CLI_NAME_LOWER,
ICON_PATH,
PROJECT_NAME,
PROJECT_NAME_LOWER,
)
notification.notify( # type: ignore
title=f"{PROJECT_NAME} notification".title(),
message=f"Nothing was selected {PROJECT_NAME_LOWER} is shutting down",
app_name=PROJECT_NAME,
title=f"{CLI_NAME} notification".title(),
message=f"Nothing was selected {CLI_NAME_LOWER} is shutting down",
app_name=CLI_NAME,
app_icon=str(ICON_PATH),
timeout=2 * 60,
)
@@ -120,15 +120,15 @@ class RofiSelector(BaseSelector):
from plyer import notification
from ....core.constants import (
CLI_NAME,
CLI_NAME_LOWER,
ICON_PATH,
PROJECT_NAME,
PROJECT_NAME_LOWER,
)
notification.notify( # type: ignore
title=f"{PROJECT_NAME} notification".title(),
message=f"Nothing was selected {PROJECT_NAME_LOWER} is shutting down",
app_name=PROJECT_NAME,
title=f"{CLI_NAME} notification".title(),
message=f"Nothing was selected {CLI_NAME_LOWER} is shutting down",
app_name=CLI_NAME,
app_icon=str(ICON_PATH),
timeout=2 * 60,
)

View File

@@ -14,8 +14,18 @@ class SelectorFactory:
"""
Factory to create a selector instance based on the configuration.
"""
from ...core.plugins.manager import plugin_manager
selector_name = config.general.selector
# Check if it's a plugin first
if plugin_manager.is_plugin("selector", selector_name):
try:
return plugin_manager.load_component("selector", selector_name)
except Exception as e:
raise ValueError(f"Could not load plugin selector '{selector_name}': {e}") from e
# Handle built-in selectors
if selector_name not in SELECTORS:
raise ValueError(
f"Unsupported selector: '{selector_name}'.Available selectors are: {SELECTORS}"