betterDXcam


NamebetterDXcam JSON
Version 0.0.9 PyPI version JSON
download
home_pagehttps://github.com/E1Bos/betterDXcam
SummaryA Python high-performance screenshot library for Windows use Desktop Duplication API
upload_time2024-06-14 16:53:10
maintainerNone
docs_urlNone
authorE1Bos
requires_python>=3.7
licenseMIT
keywords screen screenshot screencapture screengrab windows
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # **Better DXcam**
> ***Fastest Python Screenshot for Windows, Forked, and Maintained***
```python
import betterdxcam
camera = betterdxcam.create()
camera.grab()
```

## Introduction
BetterDXcam is a fork of [DXcam](https://github.com/ra1nty/DXcam), a Python high-performance screenshot library for Windows using Desktop Duplication API. Capable of 240Hz+ capturing. It was originally built as a part of deep learning pipeline for FPS games to perform better than existed python solutions ([python-mss](https://github.com/BoboTiG/python-mss), [D3DShot](https://github.com/SerpentAI/D3DShot/)). 

BetterDXcam provides these improvements over DXcam:
- Fixed crashing when screen changes resolution

Compared to these existed solutions, DXcam provides:
- Way faster screen capturing speed (> 240Hz)
- Capturing of Direct3D exclusive full-screen application without interrupting, even when alt+tab.
- Automatic handling of scaled / stretched resolution.
- Accurate FPS targeting when in capturing mode, makes it suitable for Video output. 
- Seamless integration with NumPy, OpenCV, PyTorch, etc.

## Installation
### From PyPI:
```bash
pip install betterdxcam
```

**Note:** OpenCV is required by betterDXcam for colorspace conversion. If you don't already have OpenCV, install it easily with command `pip install betterdxcam[cv2]`.

### From source:
```bash
pip install --editable .

# for installing OpenCV also
pip install --editable .[cv2]
```

## Usage
In betterDXCam, each output (monitor) is asscociated to a ```betterDXCamera``` instance.
To create a betterDXCamera instance:
```python
import betterdxcam
camera = betterdxcam.create()  # returns a betterDXCamera instance on primary monitor
```
### Screenshot
For screenshot, simply use ```.grab```:
```python
frame = camera.grab()
```
The returned ```frame``` will be a ```numpy.ndarray``` in the shape of ```(Height,  Width, 3[RGB])```. This is the default and the only supported format (**for now**). It is worth noting that ```.grab``` will return ```None``` if there is no new frame since the last time you called ```.grab```. Usually it means there's nothing new to render since last time (E.g. You are idling).

To view the captured screenshot:
```python
from PIL import Image
Image.fromarray(frame).show()
```
To screenshot a specific region, use the ```region``` parameter: it takes ```tuple[int, int, int, int]``` as the left, top, right, bottom coordinates of the bounding box. Similar to [PIL.ImageGrab.grab](https://pillow.readthedocs.io/en/stable/reference/ImageGrab.html).
```python
left, top = (1920 - 640) // 2, (1080 - 640) // 2
right, bottom = left + 640, top + 640
region = (left, top, right, bottom)
frame = camera.grab(region=region)  # numpy.ndarray of size (640x640x3) -> (HXWXC)
```
The above code will take a screenshot of the center ```640x640``` portion of a ```1920x1080``` monitor.
### Screen Capture
To start a screen capture, simply use ```.start```: the capture will be started in a separated thread, default at 60Hz. Use ```.stop``` to stop the capture.
```python
camera.start(region=(left, top, right, bottom))  # Optional argument to capture a region
camera.is_capturing  # True
# ... Do Something
camera.stop()
camera.is_capturing  # False
```
### Consume the Screen Capture Data
While the ```betterDXCamera``` instance is in capture mode, you can use ```.get_latest_frame``` to get the latest frame in the frame buffer:
```python
camera.start()
for i in range(1000):
    image = camera.get_latest_frame()  # Will block until new frame available
camera.stop()
```
Notice that ```.get_latest_frame``` by default will block until there is a new frame available since the last call to ```.get_latest_frame```. To change this behavior, use ```video_mode=True```.

## Advanced Usage and Remarks
### Multiple monitors / GPUs
```python
cam1 = betterdxcam.create(device_idx=0, output_idx=0)
cam2 = betterdxcam.create(device_idx=0, output_idx=1)
cam3 = betterdxcam.create(device_idx=1, output_idx=1)
img1 = cam1.grab()
img2 = cam2.grab()
img2 = cam3.grab()
```
The above code creates three ```betterDXCamera``` instances for: ```[monitor0, GPU0], [monitor1, GPU0], [monitor1, GPU1]```, and subsequently takes three full-screen screenshots. (cross GPU untested, but I hope it works.) To get a complete list of devices and outputs:
```pycon
>>> import betterdxcam
>>> betterdxcam.device_info()
'Device[0]:<Device Name:NVIDIA GeForce RTX 3090 Dedicated VRAM:24348Mb VendorId:4318>\n'
>>> betterdxcam.output_info()
'Device[0] Output[0]: Res:(1920, 1080) Rot:0 Primary:True\nDevice[0] Output[1]: Res:(1920, 1080) Rot:0 Primary:False\n'
```

### Output Format
You can specify the output color mode upon creation of the betterDXCamera instance:
```python
betterdxcam.create(output_idx=0, output_color="BGRA")
```
We currently support "RGB", "RGBA", "BGR", "BGRA", "GRAY", with "GRAY being the gray scale. As for the data format, ```betterDXCamera``` only supports ```numpy.ndarray```  in shape of ```(Height, Width, Channels)``` right now. ***We will soon add support for other output formats.***

### Video Buffer
The captured frames will be insert into a fixed-size ring buffer, and when the buffer is full the newest frame will replace the oldest frame. You can specify the max buffer length (defualt to 64) using the argument ```max_buffer_len``` upon creation of the ```betterDXCamera``` instance. 
```python
camera = betterdxcam.create(max_buffer_len=512)
```
***Note:  Right now to consume frames during capturing there is only `get_latest_frame` available which assume the user to process frames in a LIFO pattern. This is a read-only action and won't pop the processed frame from the buffer. we will make changes to support various of consuming pattern soon.***

### Target FPS
To make ```betterDXCamera``` capture close to the user specified ```target_fps```, we used the undocumented ```CREATE_WAITABLE_TIMER_HIGH_RESOLUTION ``` flag to create a Windows [Waitable Timer Object](https://docs.microsoft.com/en-us/windows/win32/sync/waitable-timer-objects). This is far more accurate (+/- 1ms) than Python (<3.11) ```time.sleep``` (min resolution 16ms). The implementation is done through ```ctypes``` creating a perodic timer. Python 3.11 used a similar approach[^2]. 
```python
camera.start(target_fps=120)  # Should not be made greater than 160.
```
However, due to Windows itself is a preemptive OS[^1] and the overhead of Python calls, the target FPS can not be guarenteed accurate when greater than 160. (See Benchmarks)


### Video Mode
The default behavior of ```.get_latest_frame``` only put newly rendered frame in the buffer, which suits the usage scenario of a object detection/machine learning pipeline. However, when recording a video that is not ideal since we aim to get the frames at a constant framerate: When the ```video_mode=True``` is specified when calling ```.start``` method of a ```betterDXCamera``` instance, the frame buffer will be feeded at the target fps, using the last frame if there is no new frame available. For example, the following code output a 5-second, 120Hz screen capture:
```python
target_fps = 120
camera = betterdxcam.create(output_idx=0, output_color="BGR")
camera.start(target_fps=target_fps, video_mode=True)
writer = cv2.VideoWriter(
    "video.mp4", cv2.VideoWriter_fourcc(*"mp4v"), target_fps, (1920, 1080)
)
for i in range(600):
    writer.write(camera.get_latest_frame())
camera.stop()
writer.release()
```
> You can do interesting stuff with libraries like ```pyav``` and ```pynput```: see examples/instant_replay.py for a ghetto implementation of instant replay using hot-keys


### Safely Releasing of Resource
Upon calling ```.release``` on a betterDXCamera instance, it will stop any active capturing, free the buffer and release the duplicator and staging resource. Upon calling ```.stop()```, betterDXCamera will stop the active capture and free the frame buffer. If you want to manually recreate a ```betterDXCamera``` instance on the same output with different parameters, you can also manully delete it:
```python
camera1 = betterdxcam.create(output_idx=0, output_color="BGR")
camera2 = betterdxcam.create(output_idx=0)  # Not allowed, camera1 will be returned
camera1 is camera2  # True
del camera1
del camera2
camera2 = betterdxcam.create(output_idx=0)  # Allowed
```

## Benchmarks
### For Max FPS Capability:
```python
start_time, fps = time.perf_counter(), 0
cam = betterdxcam.create()
start = time.perf_counter()
while fps < 1000:
    frame = cam.grab()
    if frame is not None:  # New frame
        fps += 1
end_time = time.perf_counter() - start_time
print(f"{title}: {fps/end_time}")
```
When using a similar logistic (only captured new frame counts), ```betterDXcam / DXcam, python-mss, D3DShot``` benchmarked as follow:

|             | DXcam  | python-mss | D3DShot |
|-------------|--------|------------|---------|
| Average FPS | 238.79 :checkered_flag: | 75.87      | 118.36  |
| Std Dev     | 1.25   | 0.5447     | 0.3224   |

The benchmark is across 5 runs, with a light-moderate usage on my PC (5900X + 3090; Chrome ~30tabs, VS Code opened, etc.), I used the [Blur Buster UFO test](https://www.testufo.com/framerates#count=5&background=stars&pps=960) to constantly render 240 fps on my monitor (Zowie 2546K). DXcam captured almost every frame rendered.

### For Targeting FPS:
```python
camera = betterdxcam.create(output_idx=0)
camera.start(target_fps=60)
for i in range(1000):
    image = camera.get_latest_frame()
camera.stop()
```
|   (Target)\\(mean,std)          | betterDXcam / DXcam  | python-mss | D3DShot |
|-------------  |--------                 |------------|---------|
| 60fps         | 61.71, 0.26 :checkered_flag: | N/A     | 47.11, 1.33  |
| 30fps         | 30.08, 0.02 :checkered_flag:  | N/A     | 21.24, 0.17  |

## Work Referenced
[D3DShot](https://github.com/SerpentAI/D3DShot/) : DXcam (and by extension betterDXcam) borrows the ctypes header directly from the no-longer maintained D3DShot.

[OBS Studio](https://github.com/obsproject/obs-studio) : Learned a lot from it.


[^1]: <https://en.wikipedia.org/wiki/Preemption_(computing)> Preemption (computing)

[^2]: <https://github.com/python/cpython/issues/65501> bpo-21302: time.sleep() uses waitable timer on Windows

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/E1Bos/betterDXcam",
    "name": "betterDXcam",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": null,
    "keywords": "screen, screenshot, screencapture, screengrab, windows",
    "author": "E1Bos",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/ac/4a/e9d56795e66bb88bb03ac21fb5675d82d2d275726fdc7b3ea94362f6026b/betterdxcam-0.0.9.tar.gz",
    "platform": null,
    "description": "# **Better DXcam**\r\n> ***Fastest Python Screenshot for Windows, Forked, and Maintained***\r\n```python\r\nimport betterdxcam\r\ncamera = betterdxcam.create()\r\ncamera.grab()\r\n```\r\n\r\n## Introduction\r\nBetterDXcam is a fork of [DXcam](https://github.com/ra1nty/DXcam), a Python high-performance screenshot library for Windows using Desktop Duplication API. Capable of 240Hz+ capturing. It was originally built as a part of deep learning pipeline for FPS games to perform better than existed python solutions ([python-mss](https://github.com/BoboTiG/python-mss), [D3DShot](https://github.com/SerpentAI/D3DShot/)). \r\n\r\nBetterDXcam provides these improvements over DXcam:\r\n- Fixed crashing when screen changes resolution\r\n\r\nCompared to these existed solutions, DXcam provides:\r\n- Way faster screen capturing speed (> 240Hz)\r\n- Capturing of Direct3D exclusive full-screen application without interrupting, even when alt+tab.\r\n- Automatic handling of scaled / stretched resolution.\r\n- Accurate FPS targeting when in capturing mode, makes it suitable for Video output. \r\n- Seamless integration with NumPy, OpenCV, PyTorch, etc.\r\n\r\n## Installation\r\n### From PyPI:\r\n```bash\r\npip install betterdxcam\r\n```\r\n\r\n**Note:** OpenCV is required by betterDXcam for colorspace conversion. If you don't already have OpenCV, install it easily with command `pip install betterdxcam[cv2]`.\r\n\r\n### From source:\r\n```bash\r\npip install --editable .\r\n\r\n# for installing OpenCV also\r\npip install --editable .[cv2]\r\n```\r\n\r\n## Usage\r\nIn betterDXCam, each output (monitor) is asscociated to a ```betterDXCamera``` instance.\r\nTo create a betterDXCamera instance:\r\n```python\r\nimport betterdxcam\r\ncamera = betterdxcam.create()  # returns a betterDXCamera instance on primary monitor\r\n```\r\n### Screenshot\r\nFor screenshot, simply use ```.grab```:\r\n```python\r\nframe = camera.grab()\r\n```\r\nThe returned ```frame``` will be a ```numpy.ndarray``` in the shape of ```(Height,  Width, 3[RGB])```. This is the default and the only supported format (**for now**). It is worth noting that ```.grab``` will return ```None``` if there is no new frame since the last time you called ```.grab```. Usually it means there's nothing new to render since last time (E.g. You are idling).\r\n\r\nTo view the captured screenshot:\r\n```python\r\nfrom PIL import Image\r\nImage.fromarray(frame).show()\r\n```\r\nTo screenshot a specific region, use the ```region``` parameter: it takes ```tuple[int, int, int, int]``` as the left, top, right, bottom coordinates of the bounding box. Similar to [PIL.ImageGrab.grab](https://pillow.readthedocs.io/en/stable/reference/ImageGrab.html).\r\n```python\r\nleft, top = (1920 - 640) // 2, (1080 - 640) // 2\r\nright, bottom = left + 640, top + 640\r\nregion = (left, top, right, bottom)\r\nframe = camera.grab(region=region)  # numpy.ndarray of size (640x640x3) -> (HXWXC)\r\n```\r\nThe above code will take a screenshot of the center ```640x640``` portion of a ```1920x1080``` monitor.\r\n### Screen Capture\r\nTo start a screen capture, simply use ```.start```: the capture will be started in a separated thread, default at 60Hz. Use ```.stop``` to stop the capture.\r\n```python\r\ncamera.start(region=(left, top, right, bottom))  # Optional argument to capture a region\r\ncamera.is_capturing  # True\r\n# ... Do Something\r\ncamera.stop()\r\ncamera.is_capturing  # False\r\n```\r\n### Consume the Screen Capture Data\r\nWhile the ```betterDXCamera``` instance is in capture mode, you can use ```.get_latest_frame``` to get the latest frame in the frame buffer:\r\n```python\r\ncamera.start()\r\nfor i in range(1000):\r\n    image = camera.get_latest_frame()  # Will block until new frame available\r\ncamera.stop()\r\n```\r\nNotice that ```.get_latest_frame``` by default will block until there is a new frame available since the last call to ```.get_latest_frame```. To change this behavior, use ```video_mode=True```.\r\n\r\n## Advanced Usage and Remarks\r\n### Multiple monitors / GPUs\r\n```python\r\ncam1 = betterdxcam.create(device_idx=0, output_idx=0)\r\ncam2 = betterdxcam.create(device_idx=0, output_idx=1)\r\ncam3 = betterdxcam.create(device_idx=1, output_idx=1)\r\nimg1 = cam1.grab()\r\nimg2 = cam2.grab()\r\nimg2 = cam3.grab()\r\n```\r\nThe above code creates three ```betterDXCamera``` instances for: ```[monitor0, GPU0], [monitor1, GPU0], [monitor1, GPU1]```, and subsequently takes three full-screen screenshots. (cross GPU untested, but I hope it works.) To get a complete list of devices and outputs:\r\n```pycon\r\n>>> import betterdxcam\r\n>>> betterdxcam.device_info()\r\n'Device[0]:<Device Name:NVIDIA GeForce RTX 3090 Dedicated VRAM:24348Mb VendorId:4318>\\n'\r\n>>> betterdxcam.output_info()\r\n'Device[0] Output[0]: Res:(1920, 1080) Rot:0 Primary:True\\nDevice[0] Output[1]: Res:(1920, 1080) Rot:0 Primary:False\\n'\r\n```\r\n\r\n### Output Format\r\nYou can specify the output color mode upon creation of the betterDXCamera instance:\r\n```python\r\nbetterdxcam.create(output_idx=0, output_color=\"BGRA\")\r\n```\r\nWe currently support \"RGB\", \"RGBA\", \"BGR\", \"BGRA\", \"GRAY\", with \"GRAY being the gray scale. As for the data format, ```betterDXCamera``` only supports ```numpy.ndarray```  in shape of ```(Height, Width, Channels)``` right now. ***We will soon add support for other output formats.***\r\n\r\n### Video Buffer\r\nThe captured frames will be insert into a fixed-size ring buffer, and when the buffer is full the newest frame will replace the oldest frame. You can specify the max buffer length (defualt to 64) using the argument ```max_buffer_len``` upon creation of the ```betterDXCamera``` instance. \r\n```python\r\ncamera = betterdxcam.create(max_buffer_len=512)\r\n```\r\n***Note:  Right now to consume frames during capturing there is only `get_latest_frame` available which assume the user to process frames in a LIFO pattern. This is a read-only action and won't pop the processed frame from the buffer. we will make changes to support various of consuming pattern soon.***\r\n\r\n### Target FPS\r\nTo make ```betterDXCamera``` capture close to the user specified ```target_fps```, we used the undocumented ```CREATE_WAITABLE_TIMER_HIGH_RESOLUTION ``` flag to create a Windows [Waitable Timer Object](https://docs.microsoft.com/en-us/windows/win32/sync/waitable-timer-objects). This is far more accurate (+/- 1ms) than Python (<3.11) ```time.sleep``` (min resolution 16ms). The implementation is done through ```ctypes``` creating a perodic timer. Python 3.11 used a similar approach[^2]. \r\n```python\r\ncamera.start(target_fps=120)  # Should not be made greater than 160.\r\n```\r\nHowever, due to Windows itself is a preemptive OS[^1] and the overhead of Python calls, the target FPS can not be guarenteed accurate when greater than 160. (See Benchmarks)\r\n\r\n\r\n### Video Mode\r\nThe default behavior of ```.get_latest_frame``` only put newly rendered frame in the buffer, which suits the usage scenario of a object detection/machine learning pipeline. However, when recording a video that is not ideal since we aim to get the frames at a constant framerate: When the ```video_mode=True``` is specified when calling ```.start``` method of a ```betterDXCamera``` instance, the frame buffer will be feeded at the target fps, using the last frame if there is no new frame available. For example, the following code output a 5-second, 120Hz screen capture:\r\n```python\r\ntarget_fps = 120\r\ncamera = betterdxcam.create(output_idx=0, output_color=\"BGR\")\r\ncamera.start(target_fps=target_fps, video_mode=True)\r\nwriter = cv2.VideoWriter(\r\n    \"video.mp4\", cv2.VideoWriter_fourcc(*\"mp4v\"), target_fps, (1920, 1080)\r\n)\r\nfor i in range(600):\r\n    writer.write(camera.get_latest_frame())\r\ncamera.stop()\r\nwriter.release()\r\n```\r\n> You can do interesting stuff with libraries like ```pyav``` and ```pynput```: see examples/instant_replay.py for a ghetto implementation of instant replay using hot-keys\r\n\r\n\r\n### Safely Releasing of Resource\r\nUpon calling ```.release``` on a betterDXCamera instance, it will stop any active capturing, free the buffer and release the duplicator and staging resource. Upon calling ```.stop()```, betterDXCamera will stop the active capture and free the frame buffer. If you want to manually recreate a ```betterDXCamera``` instance on the same output with different parameters, you can also manully delete it:\r\n```python\r\ncamera1 = betterdxcam.create(output_idx=0, output_color=\"BGR\")\r\ncamera2 = betterdxcam.create(output_idx=0)  # Not allowed, camera1 will be returned\r\ncamera1 is camera2  # True\r\ndel camera1\r\ndel camera2\r\ncamera2 = betterdxcam.create(output_idx=0)  # Allowed\r\n```\r\n\r\n## Benchmarks\r\n### For Max FPS Capability:\r\n```python\r\nstart_time, fps = time.perf_counter(), 0\r\ncam = betterdxcam.create()\r\nstart = time.perf_counter()\r\nwhile fps < 1000:\r\n    frame = cam.grab()\r\n    if frame is not None:  # New frame\r\n        fps += 1\r\nend_time = time.perf_counter() - start_time\r\nprint(f\"{title}: {fps/end_time}\")\r\n```\r\nWhen using a similar logistic (only captured new frame counts), ```betterDXcam / DXcam, python-mss, D3DShot``` benchmarked as follow:\r\n\r\n|             | DXcam  | python-mss | D3DShot |\r\n|-------------|--------|------------|---------|\r\n| Average FPS | 238.79 :checkered_flag: | 75.87      | 118.36  |\r\n| Std Dev     | 1.25   | 0.5447     | 0.3224   |\r\n\r\nThe benchmark is across 5 runs, with a light-moderate usage on my PC (5900X + 3090; Chrome ~30tabs, VS Code opened, etc.), I used the [Blur Buster UFO test](https://www.testufo.com/framerates#count=5&background=stars&pps=960) to constantly render 240 fps on my monitor (Zowie 2546K). DXcam captured almost every frame rendered.\r\n\r\n### For Targeting FPS:\r\n```python\r\ncamera = betterdxcam.create(output_idx=0)\r\ncamera.start(target_fps=60)\r\nfor i in range(1000):\r\n    image = camera.get_latest_frame()\r\ncamera.stop()\r\n```\r\n|   (Target)\\\\(mean,std)          | betterDXcam / DXcam  | python-mss | D3DShot |\r\n|-------------  |--------                 |------------|---------|\r\n| 60fps         | 61.71, 0.26 :checkered_flag: | N/A     | 47.11, 1.33  |\r\n| 30fps         | 30.08, 0.02 :checkered_flag:  | N/A     | 21.24, 0.17  |\r\n\r\n## Work Referenced\r\n[D3DShot](https://github.com/SerpentAI/D3DShot/) : DXcam (and by extension betterDXcam) borrows the ctypes header directly from the no-longer maintained D3DShot.\r\n\r\n[OBS Studio](https://github.com/obsproject/obs-studio) : Learned a lot from it.\r\n\r\n\r\n[^1]: <https://en.wikipedia.org/wiki/Preemption_(computing)> Preemption (computing)\r\n\r\n[^2]: <https://github.com/python/cpython/issues/65501> bpo-21302: time.sleep() uses waitable timer on Windows\r\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A Python high-performance screenshot library for Windows use Desktop Duplication API",
    "version": "0.0.9",
    "project_urls": {
        "Homepage": "https://github.com/E1Bos/betterDXcam",
        "Source": "https://github.com/E1Bos/betterdxcam",
        "Tracker": "https://github.com/E1Bos/betterdxcam/issues"
    },
    "split_keywords": [
        "screen",
        " screenshot",
        " screencapture",
        " screengrab",
        " windows"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "c2d219bf81e23c96aa00bea73b72bdb9cb007dfd0c37fdaeca8f6e3942e133a1",
                "md5": "9813b7ed09d0e3af66651c8cd7edc3b1",
                "sha256": "37c38a75805c9074abe364c561aaa4cbf1a5aa2b48b09cd2c7a70695ed9b6253"
            },
            "downloads": -1,
            "filename": "betterDXcam-0.0.9-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "9813b7ed09d0e3af66651c8cd7edc3b1",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 21681,
            "upload_time": "2024-06-14T16:53:08",
            "upload_time_iso_8601": "2024-06-14T16:53:08.642261Z",
            "url": "https://files.pythonhosted.org/packages/c2/d2/19bf81e23c96aa00bea73b72bdb9cb007dfd0c37fdaeca8f6e3942e133a1/betterDXcam-0.0.9-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ac4ae9d56795e66bb88bb03ac21fb5675d82d2d275726fdc7b3ea94362f6026b",
                "md5": "e6d714ce9207a8f3a7b92a1b5a1141e9",
                "sha256": "5ee82bc4ed565f0bd5d4ac95f842c751d4a9995d9a03265d3a7af37a24c471e5"
            },
            "downloads": -1,
            "filename": "betterdxcam-0.0.9.tar.gz",
            "has_sig": false,
            "md5_digest": "e6d714ce9207a8f3a7b92a1b5a1141e9",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 18020,
            "upload_time": "2024-06-14T16:53:10",
            "upload_time_iso_8601": "2024-06-14T16:53:10.382761Z",
            "url": "https://files.pythonhosted.org/packages/ac/4a/e9d56795e66bb88bb03ac21fb5675d82d2d275726fdc7b3ea94362f6026b/betterdxcam-0.0.9.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-06-14 16:53:10",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "E1Bos",
    "github_project": "betterDXcam",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "betterdxcam"
}
        
Elapsed time: 0.30160s