Compare commits
316 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5bac458c0 | ||
|
|
d96879011b | ||
|
|
da25bd34a7 | ||
|
|
d16989dab5 | ||
|
|
cfcd266265 | ||
|
|
edf405eda6 | ||
|
|
2e63d75ddd | ||
|
|
4a2cf013df | ||
|
|
45382d252a | ||
|
|
ce7365551c | ||
|
|
4fb1d7b931 | ||
|
|
74d95d2437 | ||
|
|
a4fecc66ea | ||
|
|
74549c5353 | ||
|
|
a1e3873063 | ||
|
|
7e1c186c2e | ||
|
|
567e9311d9 | ||
|
|
5d9143c7e2 | ||
|
|
6af1e80efb | ||
|
|
90e5fe3ac6 | ||
|
|
a3cd65827f | ||
|
|
e8838676b2 | ||
|
|
c31ca93c53 | ||
|
|
9b46a495d3 | ||
|
|
214662e419 | ||
|
|
47f2f9f4f7 | ||
|
|
3ec381df53 | ||
|
|
c366a4eece | ||
|
|
9d54cd6c3a | ||
|
|
865aaaef28 | ||
|
|
7616ee094f | ||
|
|
6edefa9cb8 | ||
|
|
897e4051c1 | ||
|
|
8856f648f6 | ||
|
|
000acbf8b9 | ||
|
|
bb852f382b | ||
|
|
1be8e4ed1c | ||
|
|
3d0be22e4a | ||
|
|
c38ebd4d1a | ||
|
|
531f9660f3 | ||
|
|
13231a4f61 | ||
|
|
2b990376a6 | ||
|
|
e323db3216 | ||
|
|
27418b6675 | ||
|
|
54e1a03b6b | ||
|
|
8dd1db7081 | ||
|
|
384b105346 | ||
|
|
4ceb5e78e4 | ||
|
|
b59187bd82 | ||
|
|
f386aa9d1d | ||
|
|
a8fe2cd32c | ||
|
|
6b1bc4ee06 | ||
|
|
f4b81d890c | ||
|
|
7ab0744495 | ||
|
|
0a0baebaad | ||
|
|
96e92dc295 | ||
|
|
6b163cd377 | ||
|
|
c9d30e8aca | ||
|
|
3aad6677d8 | ||
|
|
957a9d1f8f | ||
|
|
d6677623ec | ||
|
|
5febef269a | ||
|
|
7c3530cf27 | ||
|
|
d9b6211715 | ||
|
|
dab2599d29 | ||
|
|
37738e8990 | ||
|
|
4e968334ca | ||
|
|
d04902e749 | ||
|
|
4ea404f16f | ||
|
|
4d0b5db83f | ||
|
|
7f8e69fe8c | ||
|
|
c8fa3bb6c0 | ||
|
|
93406cfcbb | ||
|
|
c11854e164 | ||
|
|
0094422e9d | ||
|
|
d4d22e692b | ||
|
|
66e166c51c | ||
|
|
c51ac0bdde | ||
|
|
949ee29fa4 | ||
|
|
6e20d73b12 | ||
|
|
f629c7f3e4 | ||
|
|
6c3dbe48e9 | ||
|
|
b5dedda9a8 | ||
|
|
6e32b7b18e | ||
|
|
5fa99a17d7 | ||
|
|
563308700a | ||
|
|
716194da48 | ||
|
|
bb41c737ef | ||
|
|
bb4c3da0bf | ||
|
|
e0dac0fd99 | ||
|
|
3040965d79 | ||
|
|
3899f5f826 | ||
|
|
f82c79aa7d | ||
|
|
68ced0f2ec | ||
|
|
d1ce4c0ba4 | ||
|
|
ed7a9f5143 | ||
|
|
8f7c267153 | ||
|
|
0c68efd06e | ||
|
|
5f01b50635 | ||
|
|
cf618281d8 | ||
|
|
e3a1443b9b | ||
|
|
863f3f3128 | ||
|
|
edee97a54c | ||
|
|
e01c81aca1 | ||
|
|
f713a8ef39 | ||
|
|
bfc2c0d853 | ||
|
|
8a8e92bfef | ||
|
|
d26a521997 | ||
|
|
223f1e4b5b | ||
|
|
5ffa2d9dcc | ||
|
|
5c9c680561 | ||
|
|
c9cdc10a73 | ||
|
|
750ca3e13b | ||
|
|
a2bf1533ae | ||
|
|
7fa0e3f555 | ||
|
|
56e74d5ccb | ||
|
|
10737a91c4 | ||
|
|
d29eab943e | ||
|
|
e17fed8704 | ||
|
|
b09d2c6c1f | ||
|
|
208e6e60fc | ||
|
|
1851664c96 | ||
|
|
ab0cd6717a | ||
|
|
ccb52cc8b5 | ||
|
|
41ed2b62d4 | ||
|
|
b6ab6417cb | ||
|
|
e857912c74 | ||
|
|
ac18fbf020 | ||
|
|
494a9f8446 | ||
|
|
cec47852d8 | ||
|
|
d5a8b6f0c0 | ||
|
|
95c34277ca | ||
|
|
ca6ca1f0e6 | ||
|
|
026db54551 | ||
|
|
793b061d3f | ||
|
|
7557ae125a | ||
|
|
f94120b84c | ||
|
|
787a975009 | ||
|
|
95c1cc3cfe | ||
|
|
95176ee9de | ||
|
|
7b379b58a3 | ||
|
|
10a60ab556 | ||
|
|
38e7e8b40b | ||
|
|
6b25cc96e2 | ||
|
|
4799483b2b | ||
|
|
3e6eeffff5 | ||
|
|
00d25e89d9 | ||
|
|
f7453c7720 | ||
|
|
77d799a461 | ||
|
|
9a0d8dbc80 | ||
|
|
9f39cc4508 | ||
|
|
08dbda3b80 | ||
|
|
8317ef0bf2 | ||
|
|
b9c5833374 | ||
|
|
b6f5c41559 | ||
|
|
e665d159f6 | ||
|
|
d9bd792394 | ||
|
|
cb8ef2ae22 | ||
|
|
fa8506935f | ||
|
|
c49ba82f1b | ||
|
|
f11b2d2483 | ||
|
|
894edb4561 | ||
|
|
4c4b033aa4 | ||
|
|
f6a003d707 | ||
|
|
6a2dc80ad4 | ||
|
|
b54974e584 | ||
|
|
a2845556a4 | ||
|
|
7ce765ddb1 | ||
|
|
fc9b36d281 | ||
|
|
556d1350b8 | ||
|
|
a84d52fb38 | ||
|
|
2928168bc2 | ||
|
|
5714d71e7c | ||
|
|
170fbf6edb | ||
|
|
d2993c093c | ||
|
|
c9a2784e56 | ||
|
|
59804c9970 | ||
|
|
5955c1dc69 | ||
|
|
bfb63ebf49 | ||
|
|
396071f15a | ||
|
|
6503b442f1 | ||
|
|
5c5e2096b8 | ||
|
|
eb8bb38aa5 | ||
|
|
d486a94c6f | ||
|
|
ee932c05e3 | ||
|
|
e13a4584fb | ||
|
|
91e2efe7e1 | ||
|
|
b5442a0d94 | ||
|
|
2318c3a185 | ||
|
|
fcfdf1cbf6 | ||
|
|
f07db1b74f | ||
|
|
5d5453231f | ||
|
|
5d7afeeed3 | ||
|
|
d9f760ad97 | ||
|
|
6c54a438cb | ||
|
|
9d4f8c83b4 | ||
|
|
9b8c79b232 | ||
|
|
50151c4f3f | ||
|
|
5c7fe2352e | ||
|
|
7c31f988c8 | ||
|
|
fce36ee279 | ||
|
|
9463dc3e88 | ||
|
|
5986cf32ff | ||
|
|
64980e71df | ||
|
|
b8c7979c78 | ||
|
|
7f34488ad2 | ||
|
|
c553d9ae90 | ||
|
|
8599683cc7 | ||
|
|
d026a245ed | ||
|
|
f12c5df41c | ||
|
|
79c01e5b88 | ||
|
|
60de43cd44 | ||
|
|
c57d6a681f | ||
|
|
dd537ccbf4 | ||
|
|
ed9ab3af07 | ||
|
|
99291ed2be | ||
|
|
3ea20f245c | ||
|
|
bd5be0239d | ||
|
|
ec05910347 | ||
|
|
bc4d2cafc9 | ||
|
|
2f8db3b99d | ||
|
|
860a472e9b | ||
|
|
9f20d10b4b | ||
|
|
fa2b75ac63 | ||
|
|
9d7ce50b94 | ||
|
|
8daec6f46e | ||
|
|
c663a2de8f | ||
|
|
b77e8cc190 | ||
|
|
9fd3333a3a | ||
|
|
0ae5f296e8 | ||
|
|
77cdf7e41f | ||
|
|
febbbe2cf5 | ||
|
|
15654d2e44 | ||
|
|
2cb74cca41 | ||
|
|
c175909227 | ||
|
|
cc363b4c57 | ||
|
|
e196c76db4 | ||
|
|
07416678a0 | ||
|
|
e4d0bb418d | ||
|
|
7069156f54 | ||
|
|
384d98ff8b | ||
|
|
7f314cedd8 | ||
|
|
8da53b5daf | ||
|
|
ce2fe77de7 | ||
|
|
d5d7cf29ae | ||
|
|
612c0594f1 | ||
|
|
29d69a2919 | ||
|
|
437cb47654 | ||
|
|
0b2be8defe | ||
|
|
fa622a1c70 | ||
|
|
16c425ed56 | ||
|
|
e56fef8df8 | ||
|
|
d10f2494f9 | ||
|
|
05a78992ce | ||
|
|
9807e3ed12 | ||
|
|
45bfe117b3 | ||
|
|
a822abbba1 | ||
|
|
1b11c4ccb5 | ||
|
|
46dda83624 | ||
|
|
259273ab16 | ||
|
|
ab878ad737 | ||
|
|
fa9324513d | ||
|
|
16aa2ddf8c | ||
|
|
3fb41a4349 | ||
|
|
7484bbe1b6 | ||
|
|
14d86db936 | ||
|
|
b4790b926a | ||
|
|
66545ac1d3 | ||
|
|
b41250c23a | ||
|
|
4e15725c4d | ||
|
|
d8545ea22b | ||
|
|
1f8d9d171b | ||
|
|
0fc8342dd0 | ||
|
|
d2bf496b2b | ||
|
|
0854a70036 | ||
|
|
f9ce96fb57 | ||
|
|
e676bb7ea7 | ||
|
|
153b1d92c0 | ||
|
|
c8cf5ba12b | ||
|
|
1ba64e6efe | ||
|
|
0ba76da085 | ||
|
|
578eda8744 | ||
|
|
b548908014 | ||
|
|
c730388fd6 | ||
|
|
d387522d1b | ||
|
|
47da32cd22 | ||
|
|
7a45e9a444 | ||
|
|
44ba0a966e | ||
|
|
2ee5480fc8 | ||
|
|
8eb0ca2043 | ||
|
|
35f63099c9 | ||
|
|
7362d80e3d | ||
|
|
18d4992afb | ||
|
|
2849dda044 | ||
|
|
5aaa8b24fc | ||
|
|
c81cd6b87d | ||
|
|
3d2fcae6dc | ||
|
|
85d73e347c | ||
|
|
6dfc23ef77 | ||
|
|
23f708a1d0 | ||
|
|
3d8bda5c41 | ||
|
|
f3f3988c59 | ||
|
|
8f0461f62c | ||
|
|
95f99df3fe | ||
|
|
340c569635 | ||
|
|
157e247416 | ||
|
|
a0bf28f4cc | ||
|
|
fd8b165212 | ||
|
|
a467955b13 | ||
|
|
eea0179b5e | ||
|
|
d20d709d6e | ||
|
|
a0c21273e8 | ||
|
|
c7ab0b8ef4 | ||
|
|
8e70ae9da9 | ||
|
|
511bb4a91c | ||
|
|
15cbd77b07 |
@@ -6,7 +6,7 @@
|
||||
"context": "..",
|
||||
//Update 'VARIANT' to pick a Python version: 3, 3.10, ...
|
||||
"args": {
|
||||
"VARIANT": "3.11"
|
||||
"VARIANT": "3.14"
|
||||
}
|
||||
},
|
||||
// Add the IDs of extensions you want installed when the container is created.
|
||||
|
||||
3
.envrc
Normal file
3
.envrc
Normal file
@@ -0,0 +1,3 @@
|
||||
source_url "https://raw.githubusercontent.com/cachix/devenv/95f329d49a8a5289d31e0982652f7058a189bfca/direnvrc" "sha256-d+8cBpDfDBj41inrADaJt+bDWhOktwslgoP5YiGJ1v0="
|
||||
|
||||
use devenv
|
||||
21
.gitattributes
vendored
Normal file
21
.gitattributes
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
* text eol=lf
|
||||
*.csv text eol=auto
|
||||
*.txt text eol=auto
|
||||
*.md text eol=auto
|
||||
*.sh text eol=lf
|
||||
*.bat text eol=crlf
|
||||
*.cmd text eol=crlf
|
||||
*.ps1 text eol=crlf
|
||||
*.png binary
|
||||
*.webp binary
|
||||
*.jpg binary
|
||||
*.jpeg binary
|
||||
*.gif binary
|
||||
*.ico binary
|
||||
*.zip binary
|
||||
*.tar binary
|
||||
*.gz binary
|
||||
*.7z binary
|
||||
*.exe binary
|
||||
*.dll binary
|
||||
*.pdf binary
|
||||
17
.github/copilot-instructions.md
vendored
Normal file
17
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# Copilot Instructions
|
||||
|
||||
This project is a web application that allows users to create and manage tasks. The application is built using Python.
|
||||
|
||||
## Coding Standards
|
||||
|
||||
- Use snakeCase for variable and function names.
|
||||
- Use PascalCase for component names.
|
||||
- Use double quotes for strings.
|
||||
- Use 4 spaces for indentation.
|
||||
|
||||
## Tone
|
||||
|
||||
- If I tell you that you are wrong, think about whether or not you think that's true and respond with facts.
|
||||
- Avoid apologizing or making conciliatory statements.
|
||||
- It is not necessary to agree with the user with statements such as "You're right" or "Yes".
|
||||
- Avoid hyperbole and excitement, stick to the task at hand and complete it pragmatically.
|
||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -8,4 +8,4 @@ updates:
|
||||
- package-ecosystem: "pip" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
interval: "daily"
|
||||
|
||||
18
.github/workflows/ci.yml
vendored
18
.github/workflows/ci.yml
vendored
@@ -10,6 +10,7 @@ on:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '**.md'
|
||||
- 'build.sh'
|
||||
# pull_request:
|
||||
schedule:
|
||||
# Run daily on default branch
|
||||
@@ -19,23 +20,27 @@ jobs:
|
||||
build:
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.x", "3.11", "3.10", "3.9", "3.8"]
|
||||
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||
# exclude:
|
||||
# 3.x is used to run code coverage
|
||||
python-version: ["3.x", "3.14", "3.13", "3.12", "3.11", "3.10"]
|
||||
platform: [ubuntu-latest, macos-latest, windows-latest, ubuntu-24.04-arm, macos-15-intel]
|
||||
# exclude:
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
check-latest: false
|
||||
check-latest: ${{ github.event_name == 'schedule' }}
|
||||
allow-prereleases: false
|
||||
- name: Install zbar shared lib for QReader (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libzbar0
|
||||
- name: Install zbar shared lib for QReader (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
@@ -52,7 +57,6 @@ jobs:
|
||||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=200 --statistics
|
||||
if: matrix.python-version != '3.7'
|
||||
- name: Type checking with mypy
|
||||
run: |
|
||||
mypy --install-types --non-interactive src/*.py tests/*.py
|
||||
|
||||
272
.github/workflows/ci_docker.yml
vendored
272
.github/workflows/ci_docker.yml
vendored
@@ -14,6 +14,7 @@ on:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '**.md'
|
||||
- 'build.sh'
|
||||
tags-ignore:
|
||||
- '**'
|
||||
# branches is needed if tags-ignore is used
|
||||
@@ -25,18 +26,29 @@ on:
|
||||
|
||||
jobs:
|
||||
build-and-push-docker-debian-image:
|
||||
name: Build Docker Bullseye image and push to repositories
|
||||
name: Build Docker Bookworm image and push to repositories
|
||||
# run only when code is compiling and tests are passing
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- DOCKER_ARCH: amd64
|
||||
platform: ubuntu-latest
|
||||
PLATFORM_ARCH: x86_64
|
||||
- DOCKER_ARCH: arm64
|
||||
platform: ubuntu-24.04-arm
|
||||
PLATFORM_ARCH: arm64
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
# steps to perform in job
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v5
|
||||
|
||||
# avoid building if there are testing errors
|
||||
- name: Run smoke test
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libzbar0
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U -r requirements-dev.txt
|
||||
@@ -44,50 +56,49 @@ jobs:
|
||||
pytest
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
# setup Docker build action
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
# Workaround for failing builds: https://github.com/docker/build-push-action/issues/761#issuecomment-1383822381
|
||||
# TODO remove workaround when fixed
|
||||
with:
|
||||
driver-opts: |
|
||||
image=moby/buildkit:v0.10.6
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
if: github.secret_source == 'Actions'
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to Github Packages
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
if: github.secret_source == 'Actions'
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GHCR_IO_TOKEN }}
|
||||
|
||||
- name: "Build image and push to Docker Hub and GitHub Container Registry"
|
||||
- name: "Build image (Bookworm/Debian 12) and push to Docker Hub and GitHub Container Registry"
|
||||
id: docker_build_qr_reader_latest
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
platforms: linux/${{ matrix.DOCKER_ARCH }}
|
||||
# relative path to the place where source code with Dockerfile is located
|
||||
# TODO file:, move to docker/
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
# builder: ${{ steps.buildx.outputs.name }}
|
||||
# Note: tags has to be all lower-case
|
||||
build-args: |
|
||||
BASE_IMAGE=python:3.14-slim-trixie
|
||||
pull: true
|
||||
tags: |
|
||||
scit0/extract_otp_secrets:latest
|
||||
scit0/extract_otp_secrets:bullseye
|
||||
ghcr.io/scito/extract_otp_secrets:latest
|
||||
ghcr.io/scito/extract_otp_secrets:bullseye
|
||||
docker.io/scit0/extract_otp_secrets:latest-${{ matrix.PLATFORM_ARCH }}
|
||||
docker.io/scit0/extract_otp_secrets:bookworm-${{ matrix.PLATFORM_ARCH }}
|
||||
ghcr.io/scito/extract_otp_secrets:latest-${{ matrix.PLATFORM_ARCH }}
|
||||
ghcr.io/scito/extract_otp_secrets:bookworm-${{ matrix.PLATFORM_ARCH }}
|
||||
provenance: true
|
||||
sbom: true
|
||||
# build on feature branches, push only on master branch
|
||||
push: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||
|
||||
@@ -98,24 +109,73 @@ jobs:
|
||||
echo "${{ steps.docker_build_qr_reader_latest.outputs.digest }}" > digests.txt
|
||||
- name: Save docker digests as artifacts
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: debian_digests
|
||||
name: digests_bookworm_${{ matrix.PLATFORM_ARCH }}
|
||||
path: digests.txt
|
||||
|
||||
create-multiarch-debian-manifests:
|
||||
name: Create multiarch manifests for Debian image
|
||||
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build-and-push-docker-debian-image
|
||||
steps:
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
if: github.secret_source == 'Actions'
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to Github Packages
|
||||
uses: docker/login-action@v3
|
||||
if: github.secret_source == 'Actions'
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GHCR_IO_TOKEN }}
|
||||
|
||||
- name: Create multiarch manifests
|
||||
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||
shell: bash
|
||||
run: |
|
||||
for tag in \
|
||||
docker.io/scit0/extract_otp_secrets:latest \
|
||||
ghcr.io/scito/extract_otp_secrets:latest \
|
||||
docker.io/scit0/extract_otp_secrets:bookworm \
|
||||
ghcr.io/scito/extract_otp_secrets:bookworm \
|
||||
; do
|
||||
docker buildx imagetools create -t $tag \
|
||||
$tag-x86_64 \
|
||||
$tag-arm64
|
||||
done
|
||||
|
||||
|
||||
build-and-push-docker-alpine-image:
|
||||
name: Build Docker Alpine image and push to repositories
|
||||
# run only when code is compiling and tests are passing
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- DOCKER_ARCH: amd64
|
||||
platform: ubuntu-latest
|
||||
PLATFORM_ARCH: x86_64
|
||||
- DOCKER_ARCH: arm64
|
||||
platform: ubuntu-24.04-arm
|
||||
PLATFORM_ARCH: arm64
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
# steps to perform in job
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v5
|
||||
|
||||
# avoid building if there are testing errors
|
||||
- name: Run smoke test
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libzbar0
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U -r requirements-dev.txt
|
||||
@@ -123,22 +183,22 @@ jobs:
|
||||
pytest
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
# setup Docker build action
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
if: github.secret_source == 'Actions'
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to Github Packages
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
if: github.secret_source == 'Actions'
|
||||
with:
|
||||
registry: ghcr.io
|
||||
@@ -147,20 +207,22 @@ jobs:
|
||||
|
||||
- name: "only_txt: Build image and push to Docker Hub and GitHub Container Registry"
|
||||
id: docker_build_only_txt
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
platforms: linux/${{ matrix.DOCKER_ARCH }}
|
||||
# relative path to the place where source code with Dockerfile is located
|
||||
platforms: linux/amd64,linux/arm64
|
||||
context: .
|
||||
file: docker/Dockerfile_only_txt
|
||||
# builder: ${{ steps.buildx.outputs.name }}
|
||||
# Note: tags has to be all lower-case
|
||||
pull: true
|
||||
tags: |
|
||||
scit0/extract_otp_secrets:only-txt
|
||||
scit0/extract_otp_secrets:alpine
|
||||
ghcr.io/scito/extract_otp_secrets:only-txt
|
||||
ghcr.io/scito/extract_otp_secrets:alpine
|
||||
docker.io/scit0/extract_otp_secrets:only-txt-${{ matrix.PLATFORM_ARCH }}
|
||||
docker.io/scit0/extract_otp_secrets:alpine-${{ matrix.PLATFORM_ARCH }}
|
||||
ghcr.io/scito/extract_otp_secrets:only-txt-${{ matrix.PLATFORM_ARCH }}
|
||||
ghcr.io/scito/extract_otp_secrets:alpine-${{ matrix.PLATFORM_ARCH }}
|
||||
provenance: true
|
||||
sbom: true
|
||||
# build on feature branches, push only on master branch
|
||||
push: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||
build-args: |
|
||||
@@ -174,24 +236,75 @@ jobs:
|
||||
|
||||
- name: Save docker digests as artifacts
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: alpine_digests
|
||||
name: digests_alpine_${{ matrix.PLATFORM_ARCH }}
|
||||
path: digests.txt
|
||||
|
||||
build-and-push-docker-buster-image:
|
||||
name: Build Docker Buster image (for PyInstsaller) and push to repositories
|
||||
# run only when code is compiling and tests are passing
|
||||
create-multiarch-alpine-manifests:
|
||||
name: Create multiarch manifests for Alpine image
|
||||
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build-and-push-docker-alpine-image
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
if: github.secret_source == 'Actions'
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to Github Packages
|
||||
uses: docker/login-action@v3
|
||||
if: github.secret_source == 'Actions'
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GHCR_IO_TOKEN }}
|
||||
|
||||
- name: Create multiarch manifests
|
||||
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||
shell: bash
|
||||
run: |
|
||||
for tag in \
|
||||
docker.io/scit0/extract_otp_secrets:only-txt \
|
||||
docker.io/scit0/extract_otp_secrets:alpine \
|
||||
ghcr.io/scito/extract_otp_secrets:only-txt \
|
||||
ghcr.io/scito/extract_otp_secrets:alpine \
|
||||
; do
|
||||
docker buildx imagetools create -t $tag \
|
||||
$tag-x86_64 \
|
||||
$tag-arm64
|
||||
done
|
||||
|
||||
build-and-push-docker-bullseye-image:
|
||||
name: Build Docker Bullseye image (for PyInstsaller) and push to repositories
|
||||
# run only when code is compiling and tests are passing
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- DOCKER_ARCH: amd64
|
||||
platform: ubuntu-latest
|
||||
PLATFORM_ARCH: x86_64
|
||||
- DOCKER_ARCH: arm64
|
||||
platform: ubuntu-24.04-arm
|
||||
PLATFORM_ARCH: arm64
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
# steps to perform in job
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v5
|
||||
|
||||
# avoid building if there are testing errors
|
||||
- name: Run smoke test
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libzbar0
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U -r requirements-dev.txt
|
||||
@@ -199,50 +312,47 @@ jobs:
|
||||
pytest
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
# setup Docker build action
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
# Workaround for failing builds: https://github.com/docker/build-push-action/issues/761#issuecomment-1383822381
|
||||
# TODO remove workaround when fixed
|
||||
with:
|
||||
driver-opts: |
|
||||
image=moby/buildkit:v0.10.6
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
if: github.secret_source == 'Actions'
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to Github Packages
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
if: github.secret_source == 'Actions'
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GHCR_IO_TOKEN }}
|
||||
|
||||
- name: "Build image from Buster and push to GitHub Container Registry"
|
||||
id: docker_build_buster
|
||||
- name: "Build image from Bullseye (Debian 11) and push to GitHub Container Registry"
|
||||
id: docker_build_bullseye
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
platforms: linux/${{ matrix.DOCKER_ARCH }}
|
||||
# relative path to the place where source code with Dockerfile is located
|
||||
# TODO file:, move to docker/
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
# builder: ${{ steps.buildx.outputs.name }}
|
||||
build-args: |
|
||||
BASE_IMAGE=python:3.11-slim-buster
|
||||
BASE_IMAGE=python:3.13-slim-bullseye
|
||||
# Note: tags has to be all lower-case
|
||||
pull: true
|
||||
tags: |
|
||||
scit0/extract_otp_secrets:buster
|
||||
docker.io/scit0/extract_otp_secrets:bullseye-${{ matrix.PLATFORM_ARCH }}
|
||||
ghcr.io/scito/extract_otp_secrets:bullseye-${{ matrix.PLATFORM_ARCH }}
|
||||
provenance: true
|
||||
sbom: true
|
||||
push: ${{ github.secret_source == 'Actions' }}
|
||||
|
||||
- name: Image digest
|
||||
@@ -252,7 +362,55 @@ jobs:
|
||||
echo "${{ steps.docker_build_qr_reader_latest.outputs.digest }}" > digests.txt
|
||||
- name: Save docker digests as artifacts
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: buster_digests
|
||||
name: digests_bullseye_${{ matrix.PLATFORM_ARCH }}
|
||||
path: digests.txt
|
||||
|
||||
create-multiarch-bullseye-manifests:
|
||||
name: Create multiarch manifests for Bullseye image
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||
needs:
|
||||
- build-and-push-docker-bullseye-image
|
||||
steps:
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
if: github.secret_source == 'Actions'
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to Github Packages
|
||||
uses: docker/login-action@v3
|
||||
if: github.secret_source == 'Actions'
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GHCR_IO_TOKEN }}
|
||||
|
||||
- name:
|
||||
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||
shell: bash
|
||||
run: |
|
||||
for tag in \
|
||||
docker.io/scit0/extract_otp_secrets:bullseye \
|
||||
ghcr.io/scito/extract_otp_secrets:bullseye \
|
||||
; do
|
||||
docker buildx imagetools create -t $tag \
|
||||
$tag-x86_64 \
|
||||
$tag-arm64
|
||||
done
|
||||
|
||||
container-images-clean-up:
|
||||
name: Cleanup old container images
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Delete Container Packages
|
||||
uses: actions/delete-package-versions@v5
|
||||
if: ${{ github.secret_source == 'Actions'}}
|
||||
with:
|
||||
package-name: 'extract_otp_secrets'
|
||||
package-type: 'container'
|
||||
min-versions-to-keep: 1
|
||||
delete-only-untagged-versions: 'true'
|
||||
|
||||
124
.github/workflows/ci_release.yml
vendored
124
.github/workflows/ci_release.yml
vendored
@@ -26,7 +26,8 @@ name: release
|
||||
|
||||
# Build matrix:
|
||||
# - Linux x86_64 glibc 2.35: ubuntu-latest
|
||||
# - Linux x86_64 glibc 2.34: extract_otp_secrets:buster
|
||||
# - Linux x86_64 glibc 2.31: extract_otp_secrets:bullseye
|
||||
# - Linux x86_64 glibc 2.36: extract_otp_secrets:bookworm
|
||||
# - Windows x86_64: windows-latest
|
||||
# - MacOS x86_64: macos-11
|
||||
# - Linux x86_64 glibc 2.28: extract_otp_secrets:buster
|
||||
@@ -56,7 +57,7 @@ jobs:
|
||||
tag_message: ${{ steps.meta.outputs.tag_message }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v5
|
||||
- name: Set meta data
|
||||
id: meta
|
||||
# Writing to env with >> $GITHUB_ENV is an alternative
|
||||
@@ -87,46 +88,50 @@ jobs:
|
||||
https://api.github.com/repos/scito/extract_otp_secrets/releases \
|
||||
--silent \
|
||||
--show-error \
|
||||
-d '{"tag_name":"${{ github.ref }}","target_commitish":"master","name":"${{ steps.meta.outputs.version }} - ${{ steps.meta.outputs.date }}","body":"${{ steps.meta.outputs.tag_message }}\n\n## Executables\n\nDownload the executable for your platform and execute it, see [README.md](https://github.com/scito/extract_otp_secrets#readme)\n\n | Executable | Description |\n | --- | --- |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_linux_x86_64 | Linux x86_64/amd64 (glibc >= 2.28) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_linux_arm64 | Linux arm64 (glibc >= 2.28) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_win_x86_64.exe | Windows x86_64/amd64/x64 |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_win_arm64.exe | N/A |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_macos_x86_64.dmg | N/A, see [README.md](https://github.com/scito/extract_otp_secrets#readme) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_macos_x86_64.pkg | N/A, see [README.md](https://github.com/scito/extract_otp_secrets#readme) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_macos_x86_64 | MacOS x86_64/amd64 (bare executable, see [README.md](https://github.com/scito/extract_otp_secrets#readme); optional libzbar must be installed manually, see [README.md](https://github.com/scito/extract_otp_secrets#readme)) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_macos_arm64 | N/A |\n","draft":true,"prerelease":false,"generate_release_notes":true}')
|
||||
-d '{"tag_name":"${{ github.ref }}","target_commitish":"master","name":"${{ steps.meta.outputs.version }} - ${{ steps.meta.outputs.date }}","body":"${{ steps.meta.outputs.tag_message }}\n\n## Executables\n\nDownload the executable for your platform and execute it, see [README.md](https://github.com/scito/extract_otp_secrets#readme)\n\n | Executable | Description |\n | --- | --- |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_linux_x86_64 | Linux x86_64/amd64 (glibc >= 2.31) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_linux_arm64 | Linux arm64 (glibc >= 2.31) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_win_x86_64.exe | Windows x86_64/amd64/x64 |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_win_arm64.exe | N/A |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_macos_x86_64.dmg | N/A, see [README.md](https://github.com/scito/extract_otp_secrets#readme) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_macos_x86_64.pkg | N/A, see [README.md](https://github.com/scito/extract_otp_secrets#readme) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_macos_x86_64 | MacOS x86_64/amd64 (bare executable, see [README.md](https://github.com/scito/extract_otp_secrets#readme); optional libzbar must be installed manually, see [README.md](https://github.com/scito/extract_otp_secrets#readme)) |\n | extract_otp_secrets${{ steps.meta.outputs.inline_version }}_macos_arm64 | N/A |\n","draft":true,"prerelease":false,"generate_release_notes":true}')
|
||||
echo upload_url=$(jq '.upload_url' <<< "$response") >> $GITHUB_OUTPUT
|
||||
echo $(jq -r '.upload_url' <<< "$response") > release_url.txt
|
||||
echo $(jq -r '.id' <<< "$response") > release_id.txt
|
||||
- name: Save Release URL File for publish
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release_url
|
||||
path: release_url.txt
|
||||
- name: Save asset upload id for publish
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release_id
|
||||
path: release_id.txt
|
||||
|
||||
build-linux-executable-in-docker:
|
||||
name: Build ${{ matrix.PLATFORM }} release in docker container
|
||||
name: Build ${{ matrix.platform }} release in docker container
|
||||
# run only when code is compiling and tests are passing
|
||||
runs-on: ubuntu-latest
|
||||
needs: create-release
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- PLATFORM: linux/amd64
|
||||
- DOCKER_PLATFORM: linux/amd64
|
||||
platform: ubuntu-latest
|
||||
EXE: extract_otp_secrets_linux_x86_64
|
||||
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_linux_x86_64
|
||||
- PLATFORM: linux/arm64
|
||||
- DOCKER_PLATFORM: linux/arm64
|
||||
platform: ubuntu-24.04-arm
|
||||
EXE: extract_otp_secrets_linux_arm64
|
||||
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_linux_arm64
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
# steps to perform in job
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v5
|
||||
|
||||
# avoid building if there are testing errors
|
||||
- name: Run smoke test
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libzbar0
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U -r requirements-dev.txt
|
||||
@@ -134,27 +139,27 @@ jobs:
|
||||
pytest
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
# setup Docker build action
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
# Workaround for failing builds: https://github.com/docker/build-push-action/issues/761#issuecomment-1383822381
|
||||
# TODO remove workaround when fixed
|
||||
with:
|
||||
driver-opts: |
|
||||
image=moby/buildkit:v0.10.6
|
||||
image=moby/buildkit:latest
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
if: github.secret_source == 'Actions'
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to Github Packages
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
if: github.secret_source == 'Actions'
|
||||
with:
|
||||
registry: ghcr.io
|
||||
@@ -164,21 +169,21 @@ jobs:
|
||||
- name: Image digest
|
||||
# TODO upload digests to assets
|
||||
run: |
|
||||
echo "extract_otp_secrets: ${{ steps.docker_build_buster.outputs.digest }}"
|
||||
echo "extract_otp_secrets: ${{ steps.docker_build_bullseye.outputs.digest }}"
|
||||
|
||||
# TODO use local docker image https://stackoverflow.com/a/61155718/1663871
|
||||
# https://github.com/multiarch/qemu-user-static
|
||||
# https://hub.docker.com/r/multiarch/qemu-user-static/
|
||||
- name: Run Pyinstaller in container for ${{ matrix.EXE }}
|
||||
run: |
|
||||
docker run --pull always --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||
docker run --platform ${{ matrix.PLATFORM }} --pull always --entrypoint /bin/bash --rm -v "$(pwd)":/files -w /files scit0/extract_otp_secrets:buster -c 'apt-get update && apt-get -y install binutils && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller -y --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name ${{ matrix.EXE }} --distpath /files/dist/ /files/src/extract_otp_secrets.py'
|
||||
docker run --pull always --entrypoint /bin/bash --rm -v "$(pwd)":/files -w /files docker.io/scit0/extract_otp_secrets:bullseye -c 'apt-get update && apt-get -y install binutils && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller -y --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name ${{ matrix.EXE }} --distpath /files/dist/ /files/src/extract_otp_secrets.py'
|
||||
|
||||
- name: Smoke tests ${{ matrix.PLATFORM }}
|
||||
if: matrix.PLATFORM == 'linux/amd64'
|
||||
- name: Smoke tests linux/amd64
|
||||
if: matrix.DOCKER_PLATFORM == 'linux/amd64'
|
||||
run: |
|
||||
dist/${{ matrix.EXE }} -V
|
||||
dist/${{ matrix.EXE }} -h
|
||||
dist/${{ matrix.EXE }} --debug
|
||||
dist/${{ matrix.EXE }} example_export.png
|
||||
dist/${{ matrix.EXE }} - < example_export.txt
|
||||
dist/${{ matrix.EXE }} --qr ZBAR example_export.png
|
||||
@@ -186,19 +191,19 @@ jobs:
|
||||
dist/${{ matrix.EXE }} --qr QREADER_DEEP example_export.png
|
||||
dist/${{ matrix.EXE }} --qr CV2 example_export.png
|
||||
dist/${{ matrix.EXE }} --qr CV2_WECHAT example_export.png
|
||||
- name: Smoke tests ${{ matrix.PLATFORM }}
|
||||
if: matrix.PLATFORM == 'linux/arm64'
|
||||
- name: Smoke tests linux/arm64
|
||||
if: matrix.DOCKER_PLATFORM == 'linux/arm64'
|
||||
run: |
|
||||
docker run --platform ${{ matrix.PLATFORM }} --pull always --entrypoint /bin/bash --rm -v "$(pwd)":/files -w /files scit0/extract_otp_secrets -c 'dist/${{ matrix.EXE }} -V && dist/${{ matrix.EXE }} -h && dist/${{ matrix.EXE }} example_export.png && dist/${{ matrix.EXE }} - < example_export.txt && dist/${{ matrix.EXE }} --qr ZBAR example_export.png && dist/${{ matrix.EXE }} --qr QREADER example_export.png && dist/${{ matrix.EXE }} --qr QREADER_DEEP example_export.png && dist/${{ matrix.EXE }} --qr CV2 example_export.png && dist/${{ matrix.EXE }} --qr CV2_WECHAT example_export.png'
|
||||
docker run --pull always --entrypoint /bin/bash --rm -v "$(pwd)":/files -w /files docker.io/scit0/extract_otp_secrets -c 'dist/${{ matrix.EXE }} -V && dist/${{ matrix.EXE }} -h && dist/${{ matrix.EXE }} example_export.png && dist/${{ matrix.EXE }} - < example_export.txt && dist/${{ matrix.EXE }} --qr ZBAR example_export.png && dist/${{ matrix.EXE }} --qr QREADER example_export.png && dist/${{ matrix.EXE }} --qr QREADER_DEEP example_export.png && dist/${{ matrix.EXE }} --qr CV2 example_export.png && dist/${{ matrix.EXE }} --qr CV2_WECHAT example_export.png'
|
||||
- name: Load Release URL File from release job
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: release_url
|
||||
- name: Display structure of files
|
||||
run: ls -R
|
||||
- name: Upload EXE to artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.EXE }}
|
||||
path: dist/${{ matrix.EXE }}
|
||||
@@ -222,6 +227,7 @@ jobs:
|
||||
needs: create-release
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#choosing-github-hosted-runners
|
||||
include:
|
||||
@@ -236,8 +242,24 @@ jobs:
|
||||
ASSET_MIME: application/vnd.microsoft.portable-executable
|
||||
UPLOAD: true
|
||||
CMD_BUILD: |
|
||||
pyinstaller -y --add-data "$($Env:pythonLocation)\__yolo_v3_qr_detector;__yolo_v3_qr_detector" --add-binary "$($Env:pythonLocation)\Lib\site-packages\pyzbar\libiconv.dll;pyzbar" --add-binary "$($Env:pythonLocation)\Lib\site-packages\pyzbar\libzbar-64.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\msvcr120.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\msvcp120.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\vcamp120.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\vcomp120.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\vccorlib120.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120u.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120chs.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120cht.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120deu.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120enu.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120esn.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120fra.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120ita.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120jpn.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120kor.dll;pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120rus.dll;pyzbar" --onefile --version-file build\win_file_version_info.txt --name extract_otp_secrets.exe src\extract_otp_secrets.py
|
||||
- os: macos-11
|
||||
pyinstaller -y --add-data "$($Env:pythonLocation)\__yolo_v3_qr_detector:__yolo_v3_qr_detector" --add-binary "$($Env:pythonLocation)\Lib\site-packages\pyzbar\libiconv.dll:pyzbar" --add-binary "$($Env:pythonLocation)\Lib\site-packages\pyzbar\libzbar-64.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\msvcr120.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\msvcp120.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\vcamp120.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\vcomp120.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\vccorlib120.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120u.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120chs.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120cht.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120deu.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120enu.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120esn.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120fra.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120ita.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120jpn.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120kor.dll:pyzbar" --add-binary "$($Env:WinDir)\system32\mfc120rus.dll:pyzbar" --onefile --version-file build\win_file_version_info.txt --name extract_otp_secrets.exe src\extract_otp_secrets.py
|
||||
- os: ubuntu-latest
|
||||
TARGET: linux
|
||||
EXE: extract_otp_secrets_ubuntu
|
||||
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_linux_x86_64_ubuntu_latest
|
||||
ASSET_MIME: application/x-executable
|
||||
UPLOAD: false
|
||||
CMD_BUILD: |
|
||||
pyinstaller -y --add-data $pythonLocation/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_ubuntu src/extract_otp_secrets.py
|
||||
- os: ubuntu-24.04-arm
|
||||
TARGET: linux
|
||||
EXE: extract_otp_secrets_ubuntu_arm64
|
||||
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_linux_arm64_ubuntu_latest
|
||||
ASSET_MIME: application/x-executable
|
||||
UPLOAD: false
|
||||
CMD_BUILD: |
|
||||
pyinstaller -y --add-data $pythonLocation/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_ubuntu_arm64 src/extract_otp_secrets.py
|
||||
- os: macos-15-intel
|
||||
TARGET: macos
|
||||
# https://pyinstaller.org/en/stable/spec-files.html#spec-file-options-for-a-macos-bundle
|
||||
EXE: extract_otp_secrets
|
||||
@@ -247,17 +269,23 @@ jobs:
|
||||
ASSET_MIME: application/octet-stream
|
||||
UPLOAD: true
|
||||
CMD_BUILD: |
|
||||
VERSION_STR=$(setuptools-git-versioning) COPYRIGHT_YEARS='2020-2023' envsubst < installer/extract_otp_secrets_macos_template.spec > extract_otp_secrets_macos.spec
|
||||
VERSION_STR=$(setuptools-git-versioning) COPYRIGHT_YEARS='2020-2025' envsubst < installer/extract_otp_secrets_macos_template.spec > extract_otp_secrets_macos.spec
|
||||
pyinstaller -y extract_otp_secrets_macos.spec
|
||||
installer/build_dmg.sh
|
||||
- os: ubuntu-latest
|
||||
TARGET: linux
|
||||
EXE: extract_otp_secrets_ubuntu
|
||||
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_linux_x86_64_ubuntu_latest
|
||||
ASSET_MIME: application/x-executable
|
||||
UPLOAD: false
|
||||
CMD_BUILD: |
|
||||
pyinstaller -y --add-data $pythonLocation/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_ubuntu src/extract_otp_secrets.py
|
||||
# Disable WARN: Cannot import pyzbar module. This problem is probably due to the missing zbar shared library.
|
||||
# - os: macos-14
|
||||
# TARGET: macos
|
||||
# # https://pyinstaller.org/en/stable/spec-files.html#spec-file-options-for-a-macos-bundle
|
||||
# EXE: extract_otp_secrets
|
||||
# ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_macos_arm64
|
||||
# DMG: extract_otp_secrets.dmg
|
||||
# ASSET_NAME_DMG: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_macos_arm64.dmg
|
||||
# ASSET_MIME: application/octet-stream
|
||||
# UPLOAD: true
|
||||
# CMD_BUILD: |
|
||||
# VERSION_STR=$(setuptools-git-versioning) COPYRIGHT_YEARS='2020-2025' envsubst < installer/extract_otp_secrets_macos_template.spec > extract_otp_secrets_macos.spec
|
||||
# pyinstaller -y extract_otp_secrets_macos.spec
|
||||
# installer/build_dmg.sh
|
||||
steps:
|
||||
- name: Output path
|
||||
if: runner.os == 'Windows'
|
||||
@@ -265,18 +293,19 @@ jobs:
|
||||
- name: List Windir
|
||||
if: runner.os == 'Windows'
|
||||
run: ls "$($Env:WinDir)\system32"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set macos macos_python_path
|
||||
# TODO use variable for Python version
|
||||
run: echo "macos_python_path=/Library/Frameworks/Python.framework/Versions/3.11" >> $GITHUB_ENV
|
||||
- name: Set up Python 3.11
|
||||
uses: actions/setup-python@v4
|
||||
run: echo "macos_python_path=/Library/Frameworks/Python.framework/Versions/3.14" >> $GITHUB_ENV
|
||||
- name: Set up Python 3.14
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.11
|
||||
python-version: 3.14
|
||||
check-latest: true
|
||||
- name: Install zbar shared lib for QReader (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libzbar0
|
||||
- name: Install zbar shared lib for QReader (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
@@ -292,7 +321,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p build/
|
||||
VERSION_STR=$(setuptools-git-versioning) VERSION_MAJOR=$(cut -d '.' -f 1 <<< "$(setuptools-git-versioning)") VERSION_MINOR=$(cut -d '.' -f 2 <<< "$(setuptools-git-versioning)") VERSION_PATCH=$(echo $(cut -d '.' -f 3 <<< "$(setuptools-git-versioning)") | sed -E -n "s/^([0-9]+).*/\1/p") VERSION_BUILD=$(echo $(cut -d '.' -f 3 <<< "$(setuptools-git-versioning)") | sed -E -n -e"s/^[0-9]+.+/99/p")$(($(git rev-list --count $(git tag | sort -V -r | sed '1!d')..HEAD))) COPYRIGHT_YEARS='2020-2023' envsubst < installer/win_file_version_info_template.txt > build/win_file_version_info.txt
|
||||
VERSION_STR=$(setuptools-git-versioning) VERSION_MAJOR=$(cut -d '.' -f 1 <<< "$(setuptools-git-versioning)") VERSION_MINOR=$(cut -d '.' -f 2 <<< "$(setuptools-git-versioning)") VERSION_PATCH=$(echo $(cut -d '.' -f 3 <<< "$(setuptools-git-versioning)") | sed -E -n "s/^([0-9]+).*/\1/p") VERSION_BUILD=$(echo $(cut -d '.' -f 3 <<< "$(setuptools-git-versioning)") | sed -E -n -e"s/^[0-9]+.+/99/p")$(($(git rev-list --count $(git tag | sort -V -r | sed '1!d')..HEAD))) COPYRIGHT_YEARS='2020-2025' envsubst < installer/win_file_version_info_template.txt > build/win_file_version_info.txt
|
||||
- name: Build with pyinstaller for ${{ matrix.TARGET }}
|
||||
env:
|
||||
# Reproducible build: https://pyinstaller.org/en/stable/advanced-topics.html#creating-a-reproducible-build
|
||||
@@ -302,6 +331,7 @@ jobs:
|
||||
run: |
|
||||
dist/${{ matrix.EXE }} -V
|
||||
dist/${{ matrix.EXE }} -h
|
||||
dist/${{ matrix.EXE }} --debug
|
||||
dist/${{ matrix.EXE }} example_export.png
|
||||
dist/${{ matrix.EXE }} --qr ZBAR example_export.png
|
||||
dist/${{ matrix.EXE }} --qr QREADER example_export.png
|
||||
@@ -314,12 +344,12 @@ jobs:
|
||||
dist/${{ matrix.EXE }} - < example_export.txt
|
||||
- name: Load Release URL File from release job
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: release_url
|
||||
- name: Load Release Id File from release job
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: release_id
|
||||
- name: Display structure of files
|
||||
@@ -332,12 +362,12 @@ jobs:
|
||||
echo "release_id=$(cat release_id.txt)" >> $GITHUB_OUTPUT
|
||||
echo "upload_url=https://uploads.github.com/repos/scito/extract_otp_secrets/releases/$(cat release_id.txt)/assets?name=" >> $GITHUB_OUTPUT
|
||||
- name: Upload EXE to artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.EXE }}
|
||||
path: dist/${{ matrix.EXE }}
|
||||
- name: Upload DMG to artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: runner.os == 'macOS'
|
||||
with:
|
||||
name: ${{ matrix.DMG }}
|
||||
@@ -362,7 +392,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Load Release Id File from release job
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: release_id
|
||||
- name: Set meta data
|
||||
|
||||
12
.github/workflows/codeql-analysis.yml
vendored
12
.github/workflows/codeql-analysis.yml
vendored
@@ -42,26 +42,26 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
@@ -69,6 +69,6 @@ jobs:
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -35,3 +35,12 @@ extract_otp_secrets.build/
|
||||
extract_otp_secrets.dist/
|
||||
extract_otp_secrets.onefile-build/
|
||||
extract_otp_secrets.bin
|
||||
# Devenv
|
||||
.devenv*
|
||||
devenv.local.nix
|
||||
|
||||
# direnv
|
||||
.direnv
|
||||
|
||||
# pre-commit
|
||||
.pre-commit-config.yaml
|
||||
|
||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -2,7 +2,7 @@
|
||||
"python.testing.pytestArgs": [
|
||||
"."
|
||||
],
|
||||
"python.testing.unittestEnabled": true,
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.pytestEnabled": true,
|
||||
"cSpell.words": [
|
||||
"devbox",
|
||||
|
||||
13
Pipfile
13
Pipfile
@@ -4,14 +4,15 @@ verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
colorama = ">=0.4.6"
|
||||
opencv-contrib-python = "*"
|
||||
colorama = "0.4.6"
|
||||
opencv-contrib-python = "4.13.0.92"
|
||||
numpy = "2.4.2"
|
||||
# for macOS: opencv-contrib-python = "<=4.7.0"
|
||||
pillow = "*"
|
||||
pyzbar = "*"
|
||||
protobuf = "*"
|
||||
protobuf = "7.34.0"
|
||||
qrcode = "*"
|
||||
qreader = "<2.0.0"
|
||||
qreader = "1.3.2"
|
||||
|
||||
[dev-packages]
|
||||
build = "*"
|
||||
@@ -24,9 +25,7 @@ pylint = "*"
|
||||
pytest = "*"
|
||||
pytest-cov = "*"
|
||||
pytest-mock = "*"
|
||||
setuptools-git-versioning = "*"
|
||||
types-protobuf = "*"
|
||||
wheel = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.11"
|
||||
python_version = "3.14"
|
||||
|
||||
1113
Pipfile.lock
generated
1113
Pipfile.lock
generated
File diff suppressed because it is too large
Load Diff
109
README.md
109
README.md
@@ -2,10 +2,10 @@
|
||||
|
||||
[](https://github.com/scito/extract_otp_secrets/actions/workflows/ci.yml)
|
||||
[](https://github.com/scito/extract_otp_secrets/actions/workflows/ci_docker.yml)
|
||||

|
||||

|
||||
[](https://github.com/scito/extract_otp_secrets/blob/master/LICENSE)
|
||||
[](https://github.com/scito/extract_otp_secrets/releases/latest)
|
||||

|
||||

|
||||
[](https://hub.docker.com/repository/docker/scit0/extract_otp_secrets/general)
|
||||
[](https://github.com/scito/extract_otp_secrets/releases/latest)
|
||||
[](https://github.com/scito/extract_otp_secrets/releases/latest)
|
||||
@@ -14,7 +14,7 @@
|
||||
[](https://stand-with-ukraine.pp.ua)
|
||||
<!-- 
|
||||
[](https://github.com/scito/extract_otp_secrets/blob/master/Pipfile.lock)
|
||||
-->
|
||||
-->
|
||||
|
||||
<!-- [](https://GitHub.com/scito/extract_otp_secrets/releases/) -->
|
||||
|
||||
@@ -36,6 +36,7 @@ The secrets can be exported to JSON or CSV, or printed as QR codes to console or
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Table of contents](#table-of-contents)
|
||||
- [Download and run binary executable (🆕 since v2.1)](#download-and-run-binary-executable--since-v21)
|
||||
- [MacOS](#macos)
|
||||
- [Usage](#usage)
|
||||
@@ -46,7 +47,7 @@ The secrets can be exported to JSON or CSV, or printed as QR codes to console or
|
||||
- [Installation of optional shared system libraries (recommended)](#installation-of-optional-shared-system-libraries-recommended)
|
||||
- [Program help: arguments and options](#program-help-arguments-and-options)
|
||||
- [Examples](#examples)
|
||||
- [Printing otp secrets from text file](#printing-otp-secrets-form-text-file)
|
||||
- [Printing otp secrets from text file](#printing-otp-secrets-from-text-file)
|
||||
- [Printing otp secrets from image file](#printing-otp-secrets-from-image-file)
|
||||
- [Writing otp secrets to csv file](#writing-otp-secrets-to-csv-file)
|
||||
- [Writing otp secrets to json file](#writing-otp-secrets-to-json-file)
|
||||
@@ -66,7 +67,8 @@ The secrets can be exported to JSON or CSV, or printed as QR codes to console or
|
||||
- [pipenv](#pipenv)
|
||||
- [Visual Studio Code Remote - Containers / VSCode devcontainer](#visual-studio-code-remote---containers--vscode-devcontainer)
|
||||
- [venv](#venv)
|
||||
- [devbox](#devbox)
|
||||
- [devbox 1](#devbox-1)
|
||||
- [devbox 2](#devbox-2)
|
||||
- [docker](#docker)
|
||||
- [More docker examples](#more-docker-examples)
|
||||
- [Tests](#tests)
|
||||
@@ -85,6 +87,7 @@ The secrets can be exported to JSON or CSV, or printed as QR codes to console or
|
||||
- [Problems and Troubleshooting](#problems-and-troubleshooting)
|
||||
- [Windows error message](#windows-error-message)
|
||||
- [Related projects](#related-projects)
|
||||
- [Third party documentation](#third-party-documentation)
|
||||
</details>
|
||||
|
||||
## Download and run binary executable (🆕 since v2.1)
|
||||
@@ -119,14 +122,23 @@ However, the bare executable can be executed from the command line:
|
||||
1. Download executable for macOS platform from [latest release](https://github.com/scito/extract_otp_secrets/releases/latest), see assets
|
||||
2. Open `Terminal` application
|
||||
3. Change to Downloads folder in Terminal: `cd $HOME/Downloads`
|
||||
4. Set executable bit for the downloaded file: `chmod +x extract_otp_secrets_X.Y.Z_macos_x86_64`
|
||||
5. Start executable from command line: `./extract_otp_secrets_X.Y.Z_macos_x86_64`
|
||||
4. Remove quarantine bit for the downloaded file: `xattr -r -d com.apple.quarantine extract_otp_secrets_X.Y.Z_macos_x86_64`
|
||||
5. Set executable bit for the downloaded file: `chmod +x extract_otp_secrets_X.Y.Z_macos_x86_64`
|
||||
6. Start executable from command line for the first time: `./extract_otp_secrets_X.Y.Z_macos_x86_64`
|
||||
7. Wait approximately 30 seconds to 1 minute on the first run. Terminal will display the following error:
|
||||
```
|
||||
OpenCV: not authorized to capture video (status 0), requesting...
|
||||
OpenCV: camera failed to properly initialize!
|
||||
```
|
||||
8. macOS will then prompt to request camera access.
|
||||
9. After allowing camera access, rerun the program.
|
||||
10. On the second run, the GUI prompt shows correctly and is fully operable: `./extract_otp_secrets_X.Y.Z_macos_x86_64`
|
||||
|
||||
:information_source: Replace `X.Y.Z` in above commands with the version number of your downloaded file, e.g. `extract_otp_secrets_2.4.0_macos_x86_64`
|
||||
|
||||
:information_source: If Rosetta2 emulation is installed, these steps work also for M1 and M2 Apple Silicon processors and the program can be executed directly.
|
||||
|
||||
> :warning: It seems the GUI mode is not working in Terminal on macOS. In tests no [GUI window](#usage) was opened. (Remarks and hints about macOS are welcome since I do not know macOS.)
|
||||
Tested with extract_otp_secrets_2.8.1_macos_x86_64 on macOS Sequoia 15.1 beta. Source: [#283](https://github.com/scito/extract_otp_secrets/issues/283)
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -204,7 +216,7 @@ For a detailed installation documentation of [pyzbar](https://github.com/Natural
|
||||
|
||||
#### Linux (Debian, Ubuntu, …)
|
||||
|
||||
sudo apt-get install libzbar0
|
||||
sudo apt-get install libgl1 libzbar0
|
||||
|
||||
#### Linux (OpenSUSE)
|
||||
|
||||
@@ -212,7 +224,7 @@ For a detailed installation documentation of [pyzbar](https://github.com/Natural
|
||||
|
||||
#### Linux (Fedora)
|
||||
|
||||
sudo dnf install zbar
|
||||
sudo dnf install mesa-libGL zbar
|
||||
|
||||
#### Linux (Arch Linux)
|
||||
|
||||
@@ -234,7 +246,7 @@ OpenCV requires [Visual C++ redistributable 2015](https://www.microsoft.com/en-u
|
||||
|
||||
## Program help: arguments and options
|
||||
|
||||
<pre>usage: extract_otp_secrets.py [-h] [--csv FILE] [--keepass FILE] [--json FILE] [--txt FILE] [--printqr] [--saveqr DIR] [--camera NUMBER] [--qr {ZBAR,QREADER,QREADER_DEEP,CV2,CV2_WECHAT}] [-i] [--no-color] [--version] [-d | -v | -q] [infile ...]
|
||||
<pre>usage: extract_otp_secrets.py [-h] [--csv FILE] [--keepass FILE] [--json FILE] [--txt FILE] [--urls FILE] [--printqr] [--saveqr DIR] [--camera NUMBER] [--qr {ZBAR,QREADER,QREADER_DEEP,CV2,CV2_WECHAT}] [-i] [--no-color] [--version] [-d | -v | -q] [infile ...]
|
||||
|
||||
Extracts one time password (OTP) secrets from QR codes exported by two-factor authentication (2FA) apps
|
||||
If no infiles are provided, a GUI window starts and QR codes are captured from the camera.
|
||||
@@ -245,10 +257,11 @@ positional arguments:
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--csv FILE, -c FILE export csv file or - for stdout
|
||||
--csv FILE, -c FILE export csv file, or - for stdout
|
||||
--keepass FILE, -k FILE export totp/hotp csv file(s) for KeePass, - for stdout
|
||||
--json FILE, -j FILE export json file or - for stdout
|
||||
--txt FILE, -t FILE export txt file or - for stdout
|
||||
--urls FILE, -u FILE export file with list of otpauth urls, or - for stdout
|
||||
--printqr, -p print QR code(s) as text to the terminal
|
||||
--saveqr DIR, -s DIR save QR code(s) as images to directory
|
||||
--camera NUMBER, -C NUMBER camera number of system (default camera: 0)
|
||||
@@ -270,7 +283,7 @@ python extract_otp_secrets.py = < example_export.png</pre>
|
||||
|
||||
## Examples
|
||||
|
||||
### Printing otp secrets form text file
|
||||
### Printing otp secrets from text file
|
||||
|
||||
python src/extract_otp_secrets.py example_export.txt
|
||||
|
||||
@@ -347,13 +360,13 @@ python extract_otp_secrets.py = < example_export.png</pre>
|
||||
* Prints errors and warnings to stderr (🆕 since v2.0)
|
||||
* Prints colored output (🆕 since v2.0)
|
||||
* Startable as executable (script, Python, and all dependencies packed in one executable) (🆕 since v2.1)
|
||||
* extract_otp_secrets_linux_x86_64 (requires glibc >= 2.28)
|
||||
* extract_otp_secrets_linux_arm64 (requires glibc >= 2.28)
|
||||
* extract_otp_secrets_linux_x86_64 (requires glibc >= 2.31)
|
||||
* extract_otp_secrets_linux_arm64 (requires glibc >= 2.31)
|
||||
* extract_otp_secrets_win_x86_64.exe
|
||||
* extract_otp_secrets_macos_x86_64 (optional [libzbar](#installation-of-optional-shared-system-libraries-recommended) needs to be installed manually if needed)
|
||||
* extract_otp_secrets_macos_x86_64.dmg N/A, see [why](#macos)
|
||||
* extract_otp_secrets_macos_x86_64.pkg N/A, see [why](#macos)
|
||||
* Prebuilt Docker images provided for amd64 and arm64 (🆕 since v2.0)
|
||||
* Prebuilt Docker images provided for amd64 and arm64 on [Docker Hub](https://hub.docker.com/repository/docker/scit0/extract_otp_secrets) and [GitHub Packages](https://github.com/users/scito/packages/container/package/extract_otp_secrets) (🆕 since v2.0)
|
||||
* Many ways to run the script:
|
||||
* Native Python
|
||||
* pipenv
|
||||
@@ -367,12 +380,12 @@ python extract_otp_secrets.py = < example_export.png</pre>
|
||||
* macOS
|
||||
* Windows
|
||||
* Uses UTF-8 on all platforms
|
||||
* Supports Python >= 3.8
|
||||
* Supports Python >= 3.10
|
||||
* Installation of shared system libraries is optional (🆕 since v2.3)
|
||||
* Provides a debug mode (-d) for analyzing import problems
|
||||
* Written in modern Python using type hints and following best practices
|
||||
* All these features are backed by tests ran nightly
|
||||
* All functionality in one Python script: src/extract_otp_secrets.py (except protobuf generated code in protobuf_generated_python)
|
||||
* All functionality in one Python script: src/extract_otp_secrets.py (except protobuf 34.0generated code in protobuf_generated_python)
|
||||
|
||||
## KeePass
|
||||
|
||||
@@ -507,7 +520,7 @@ Alternatively, you can use a python virtual env for the dependencies:
|
||||
The requirements\*.txt files contain all the dependencies (also the optional ones).
|
||||
To leave the python virtual env just call `deactivate`.
|
||||
|
||||
### devbox
|
||||
### devbox 1
|
||||
|
||||
Install [devbox](https://github.com/jetpack-io/devbox), which is a wrapper for nix. Then enter the environment with Python and the packages installed with:
|
||||
|
||||
@@ -515,6 +528,14 @@ Install [devbox](https://github.com/jetpack-io/devbox), which is a wrapper for n
|
||||
devbox shell
|
||||
```
|
||||
|
||||
### devbox 2
|
||||
|
||||
Install [devbox](https://devenv.sh), which is a wrapper for nix. Then enter the environment with Python and the packages installed with:
|
||||
|
||||
```
|
||||
devenv shell
|
||||
```
|
||||
|
||||
### docker
|
||||
|
||||
Install [Docker](https://docs.docker.com/get-docker/).
|
||||
@@ -524,35 +545,35 @@ Prebuilt docker images are available for amd64 and arm64 architectures on [Docke
|
||||
Extracting from an QR image file:
|
||||
|
||||
```
|
||||
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.png | docker run --pull always -i --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets =
|
||||
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.png | docker run --network none --pull always -i --rm -v "$(pwd)":/files:ro docker.io/scit0/extract_otp_secrets =
|
||||
```
|
||||
|
||||
Capturing from camera in GUI window (X Window system required on host):
|
||||
|
||||
```
|
||||
docker run --pull always --rm -v "$(pwd)":/files:ro -i --device="/dev/video0:/dev/video0" --env="DISPLAY" -v /tmp/.X11-unix:/tmp/.X11-unix:ro scit0/extract_otp_secrets
|
||||
docker run --network none --pull always --rm -v "$(pwd)":/files:ro -i --device="/dev/video0:/dev/video0" --env="DISPLAY" -v /tmp/.X11-unix:/tmp/.X11-unix:ro docker.io/scit0/extract_otp_secrets
|
||||
```
|
||||
|
||||
If only text processing is required, there is a small Image based on Alpine Linux:
|
||||
|
||||
```
|
||||
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.txt | docker run --pull always -i --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets:latest-only-txt -
|
||||
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.txt | docker run --network none --pull always -i --rm -v "$(pwd)":/files:ro docker.io/scit0/extract_otp_secrets:latest-only-txt -
|
||||
```
|
||||
|
||||
Docker image from GitHub:
|
||||
|
||||
```
|
||||
docker login ghcr.io -u USERNAME
|
||||
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.png | docker run --pull always -i --rm -v "$(pwd)":/files:ro ghcr.io/scito/extract_otp_secrets =
|
||||
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.png | docker run --network none --pull always -i --rm -v "$(pwd)":/files:ro ghcr.io/scito/extract_otp_secrets =
|
||||
```
|
||||
|
||||
### More docker examples
|
||||
|
||||
docker run --pull always --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets example_export.png
|
||||
docker run --network none --pull always --rm -v "$(pwd)":/files:ro docker.io/scit0/extract_otp_secrets example_export.png
|
||||
|
||||
docker run --pull always --rm -i -v "$(pwd)":/files:ro scit0/extract_otp_secrets_only_txt - < example_export.txt
|
||||
docker run --network none --pull always --rm -i -v "$(pwd)":/files:ro docker.io/scit0/extract_otp_secrets:latest-only-txt - < example_export.txt
|
||||
|
||||
cat example_export.txt | docker run --pull always --rm -i -v "$(pwd)":/files:ro scit0/extract_otp_secrets:latest_only_txt - -c - > example_out.csv
|
||||
cat example_export.txt | docker run --network none --pull always --rm -i -v "$(pwd)":/files:ro docker.io/scit0/extract_otp_secrets:latest-only-txt - -c - > example_out.csv
|
||||
|
||||
## Tests
|
||||
|
||||
@@ -674,6 +695,18 @@ There is a Bash script for a full local build including linting and type checkin
|
||||
```bash
|
||||
./build.sh
|
||||
```
|
||||
or
|
||||
```bash
|
||||
PYTHON=python3.11 PIP=pip3.11 DOCKER="sudo podman" ./build.sh -d
|
||||
```
|
||||
Linux or WSL2 Linux:
|
||||
```bash
|
||||
DOCKER="sudo podman" ./build.sh -d
|
||||
```
|
||||
Git for Windows:
|
||||
```bash
|
||||
MSYS_NO_PATHCONV=1 NOINHERIT=1 DOCKER="podman" ./build.sh -d
|
||||
```
|
||||
|
||||
The options of the build script:
|
||||
|
||||
@@ -699,16 +732,27 @@ Options:
|
||||
-h, --help Show help and quit
|
||||
```
|
||||
|
||||
#### Additional requirements
|
||||
|
||||
* envsubst
|
||||
* git
|
||||
* perl
|
||||
* pip
|
||||
|
||||
##### Fedora
|
||||
|
||||
sudo dnf install python3-pip perl envsubst
|
||||
|
||||
## Technical background
|
||||
|
||||
The export QR code of "Google Authenticator" contains the URL `otpauth-migration://offline?data=…`.
|
||||
The data parameter is a base64 encoded proto3 message (Google Protocol Buffers).
|
||||
|
||||
Command for regeneration of Python code from proto3 message definition file (only necessary in case of changes of the proto3 message definition or new protobuf versions):
|
||||
Command for regeneration of Python code from proto3 message definition file (only necessary in case of changes of the proto3 message definition or new protobuf 34.0versions):
|
||||
|
||||
protoc --plugin=protoc-gen-mypy=path/to/protoc-gen-mypy --python_out=src/protobuf_generated_python --mypy_out=src/protobuf_generated_python src/google_auth.proto
|
||||
protoc 34.0--plugin=protoc-34.0gen-mypy=path/to/protoc-34.0gen-mypy --python_out=src/protobuf_generated_python --mypy_out=src/protobuf_generated_python src/google_auth.proto
|
||||
|
||||
The generated protobuf Python code was generated by protoc 22.3 (https://github.com/protocolbuffers/protobuf/releases/tag/v22.3).
|
||||
The generated protobuf 34.0Python code was generated by protoc 34.033.1 (https://github.com/protocolbuffers/protobuf/releases/tag/v34.033.1).
|
||||
|
||||
For Python type hint generation the [mypy-protobuf](https://github.com/nipunn1313/mypy-protobuf) package is used.
|
||||
|
||||
@@ -719,7 +763,7 @@ For Python type hint generation the [mypy-protobuf](https://github.com/nipunn131
|
||||
|
||||
## Issues
|
||||
|
||||
* Segmentation fault on macOS with CV2 4.7.0: https://github.com/opencv/opencv/issues/23072
|
||||
* Segmentation fault on macOS with CV2 4.7.0: https://github.com/opencv/opencv/issues/23072 (fixed)
|
||||
* CV2 window does not show icons: https://github.com/opencv/opencv-python/issues/585
|
||||
|
||||
## Problems and Troubleshooting
|
||||
@@ -759,6 +803,11 @@ FileNotFoundError: Could not find module 'libiconv.dll' (or one of its dependenc
|
||||
* [Android OTP Extractor](https://github.com/puddly/android-otp-extractor) can extract your tokens from popular Android OTP apps and export them in a standard format or just display them as QR codes for easy importing. [Requires a _rooted_ Android phone.]
|
||||
* [Google Authenticator secret extractor](https://github.com/krissrex/google-authenticator-exporter) is similar project written in JavaScript. It also extracts otp secrets from Google Authenticator.
|
||||
|
||||
## Third party documentation
|
||||
|
||||
* [TOTP Secret Extraction from QR codes (medium.com)](https://cavalloj.medium.com/totp-secret-extraction-from-qr-codes-ee097b4c687f)
|
||||
* [Google Authenticator: OTP-Secrets auslesen (stadt-bremerhaven.de)](https://stadt-bremerhaven.de/google-authenticator-otp-secrets-auslesen/)
|
||||
|
||||
***
|
||||
|
||||
# #StandWithUkraine 🇺🇦
|
||||
|
||||
480
build.sh
480
build.sh
@@ -73,8 +73,16 @@ askContinueYn() {
|
||||
|
||||
# Reference: https://gist.github.com/steinwaywhw/a4cd19cda655b8249d908261a62687f8
|
||||
|
||||
if [[ "$MSYSTEM" == "MINGW64" || "$MSYSTEM" == "MINGW32" || "$MSYSTEM" == "UCRT64" ]]; then
|
||||
LINUX=false
|
||||
elif [[ "$(uname -s)" == MINGW* ]]; then
|
||||
LINUX=false
|
||||
else
|
||||
LINUX=true
|
||||
fi
|
||||
|
||||
interactive=false
|
||||
ignore_version_check=true
|
||||
ignore_version_check=false
|
||||
clean=false
|
||||
clean_flag=""
|
||||
build_base=true
|
||||
@@ -85,9 +93,12 @@ build_local=true
|
||||
build_exe=false
|
||||
build_nuitka_exe=false
|
||||
run_pipenv=true
|
||||
run_uv=true
|
||||
run_gui=false
|
||||
generate_result_files=false
|
||||
PYTHONHASHSEED=31
|
||||
verbose=false
|
||||
VERBOSE=''
|
||||
|
||||
while test $# -gt 0; do
|
||||
case $1 in
|
||||
@@ -101,15 +112,17 @@ while test $# -gt 0; do
|
||||
echo "-C Ignore version check of protobuf/protoc"
|
||||
echo "-e Build exe"
|
||||
echo "-n Build nuitka exe"
|
||||
echo "-L Do not build local (exe)"
|
||||
echo "-d Build docker"
|
||||
echo "-a Build arm"
|
||||
echo "-X Do not build x86_64"
|
||||
echo "-B Do not build base"
|
||||
echo "-V Do not run pipenv"
|
||||
echo "-U Do not run uv"
|
||||
echo "-L Do not run protoc and base build locally incl. exes (implies -B -V -U)"
|
||||
echo "-g Start extract_otp_secrets.py in GUI mode"
|
||||
echo "-c Clean everything"
|
||||
echo "-r Generate result files"
|
||||
echo "-v Verbose"
|
||||
echo "-h, --help Show help and quit"
|
||||
quit
|
||||
;;
|
||||
@@ -118,7 +131,7 @@ while test $# -gt 0; do
|
||||
shift
|
||||
;;
|
||||
-C)
|
||||
ignore_version_check=false
|
||||
ignore_version_check=true
|
||||
shift
|
||||
;;
|
||||
-B)
|
||||
@@ -127,6 +140,7 @@ while test $# -gt 0; do
|
||||
;;
|
||||
-L)
|
||||
build_local=false
|
||||
build_base=false
|
||||
shift
|
||||
;;
|
||||
-a)
|
||||
@@ -153,6 +167,10 @@ while test $# -gt 0; do
|
||||
run_pipenv=false
|
||||
shift
|
||||
;;
|
||||
-U)
|
||||
run_uv=false
|
||||
shift
|
||||
;;
|
||||
-g)
|
||||
run_gui=true
|
||||
shift
|
||||
@@ -161,6 +179,11 @@ while test $# -gt 0; do
|
||||
generate_result_files=true
|
||||
shift
|
||||
;;
|
||||
-v)
|
||||
verbose=true
|
||||
VERBOSE="-v"
|
||||
shift
|
||||
;;
|
||||
-c)
|
||||
clean=true
|
||||
clean_flag="--clean"
|
||||
@@ -172,11 +195,24 @@ done
|
||||
BIN="$HOME/bin"
|
||||
DOWNLOADS="$HOME/downloads"
|
||||
|
||||
PYTHON="python3.11"
|
||||
PIP="pip3.11"
|
||||
# Set python und pip if not already set in environment
|
||||
PYTHON="${PYTHON:=python}"
|
||||
PIP="${PIP:=pip}"
|
||||
UV="${UV:=uv}"
|
||||
PIPENV="$PYTHON -m pipenv"
|
||||
FLAKE8="$PYTHON -m flake8"
|
||||
MYPY="$PYTHON -m mypy"
|
||||
DOCKER="${DOCKER:=docker}"
|
||||
PYTHON_VERSION=$($PYTHON --version 2>&1 | cut -d " " -f2 | cut -d "." -f1-2)
|
||||
UVENV='.uvenv'
|
||||
|
||||
if $LINUX; then
|
||||
PWD=pwd
|
||||
UV_BIN_PATH="$UVENV/bin"
|
||||
else
|
||||
PWD="pwd -W"
|
||||
UV_BIN_PATH="$UVENV/Scripts"
|
||||
fi
|
||||
|
||||
# sudo ln -s /usr/bin/python3.11 /usr/bin/python
|
||||
|
||||
@@ -185,7 +221,11 @@ MYPY="$PYTHON -m mypy"
|
||||
DEST="protoc"
|
||||
|
||||
if $clean; then
|
||||
cmd="docker image prune -f || echo 'No docker image pruned'"
|
||||
cmd="deactivate || true"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$DOCKER image prune -f || echo 'No docker image pruned'"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
@@ -217,91 +257,111 @@ if $clean; then
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$UV cache clean || true"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="rm -rf .venv || true"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="mkdir -p dist"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
fi
|
||||
|
||||
if $build_local; then
|
||||
cmd="$PIP install --use-pep517 -U -r requirements-dev.txt"
|
||||
cmd="deactivate || true"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
echo -e "\n\nChecking Protoc version..."
|
||||
VERSION=$(curl -sL https://github.com/protocolbuffers/protobuf/releases/latest | grep -E "<title>" | perl -pe's%.*Protocol Buffers v(\d+\.\d+(\.\d+)?).*%\1%')
|
||||
BASEVERSION=4
|
||||
echo
|
||||
cmd="rm -rf .venv $UVENV || true"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$PIP install -U -r requirements-dev.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
OLDVERSION=$(cat $BIN/$DEST/.VERSION.txt || echo "")
|
||||
echo -e "\nProtoc remote version $VERSION\n"
|
||||
echo -e "Protoc local version: $OLDVERSION\n"
|
||||
|
||||
if [ "$OLDVERSION" != "$VERSION" ] || ! $ignore_version_check; then
|
||||
echo "Upgrade protoc from $OLDVERSION to $VERSION"
|
||||
|
||||
NAME="protoc-$VERSION"
|
||||
ARCHIVE="$NAME.zip"
|
||||
|
||||
mkdir -p $DOWNLOADS
|
||||
# https://github.com/protocolbuffers/protobuf/releases/download/v21.6/protoc-21.6-linux-x86_64.zip
|
||||
cmd="wget --trust-server-names https://github.com/protocolbuffers/protobuf/releases/download/v$VERSION/protoc-$VERSION-linux-x86_64.zip -O $DOWNLOADS/$ARCHIVE"
|
||||
if ! $ignore_version_check; then
|
||||
echo -e "\n\nChecking Protoc version..."
|
||||
cmd="VERSION=$(curl -sL https://github.com/protocolbuffers/protobuf/releases/latest | grep -E '<title>' | perl -pe's%.*Protocol Buffers v(\d+\.\d+(\.\d+)?).*%\1%')"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
echo
|
||||
|
||||
cmd="echo -e '\nSize [Byte]'; stat --printf='%s\n' $DOWNLOADS/$ARCHIVE; echo -e '\nMD5'; md5sum $DOWNLOADS/$ARCHIVE; echo -e '\nSHA256'; sha256sum $DOWNLOADS/$ARCHIVE;"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
OLDVERSION=$(cat $BIN/$DEST/.VERSION.txt || echo "")
|
||||
echo -e "\nProtoc remote version $VERSION\n"
|
||||
echo -e "Protoc local version: $OLDVERSION\n"
|
||||
|
||||
cmd="mkdir -p $BIN/$NAME; unzip $DOWNLOADS/$ARCHIVE -d $BIN/$NAME"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
if [ "$OLDVERSION" != "$VERSION" ]; then
|
||||
echo "Upgrade protoc from $OLDVERSION to $VERSION"
|
||||
|
||||
cmd="echo $VERSION > $BIN/$NAME/.VERSION.txt; echo $VERSION > $BIN/$NAME/.VERSION_$VERSION.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
NAME="protoc-$VERSION"
|
||||
ARCHIVE="$NAME.zip"
|
||||
|
||||
cmd="[ -d $BIN/$DEST.old ] && rm -rf $BIN/$DEST.old || echo 'No old dir to delete'"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
mkdir -p $DOWNLOADS
|
||||
# https://github.com/protocolbuffers/protobuf/releases/download/v21.6/protoc-21.6-linux-x86_64.zip
|
||||
cmd="wget --trust-server-names https://github.com/protocolbuffers/protobuf/releases/download/v$VERSION/protoc-$VERSION-linux-x86_64.zip -O $DOWNLOADS/$ARCHIVE"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="[ -d $BIN/$DEST ] && mv -iT $BIN/$DEST $BIN/$DEST.old || echo 'No previous dir to keep'"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
cmd="echo -e '\nSize [Byte]'; stat --printf='%s\n' $DOWNLOADS/$ARCHIVE; echo -e '\nMD5'; md5sum $DOWNLOADS/$ARCHIVE; echo -e '\nSHA256'; sha256sum $DOWNLOADS/$ARCHIVE;"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="mv -iT $BIN/$NAME $BIN/$DEST"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
cmd="mkdir -p $BIN/$NAME; unzip $DOWNLOADS/$ARCHIVE -d $BIN/$NAME"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="rm $DOWNLOADS/$ARCHIVE"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
cmd="echo $VERSION > $BIN/$NAME/.VERSION.txt; echo $VERSION > $BIN/$NAME/.VERSION_$VERSION.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$BIN/$DEST/bin/protoc --plugin=protoc-gen-mypy=$HOME/.local/bin/protoc-gen-mypy --python_out=src/protobuf_generated_python --mypy_out=src/protobuf_generated_python --proto_path=src google_auth.proto"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
cmd="[ -d $BIN/$DEST.old ] && rm -rf $BIN/$DEST.old || echo 'No old dir to delete'"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
# Update README.md
|
||||
cmd="[ -d $BIN/$DEST ] && mv -iT $BIN/$DEST $BIN/$DEST.old || echo 'No previous dir to keep'"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="perl -i -pe 's%proto(buf|c)([- ])(\d\.)?$OLDVERSION%proto\$1\$2\${3}$VERSION%g' README.md && perl -i -pe 's%(protobuf/releases/tag/v)$OLDVERSION%\${1}$VERSION%g' README.md"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
cmd="mv -iT $BIN/$NAME $BIN/$DEST"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="rm $DOWNLOADS/$ARCHIVE"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$BIN/$DEST/bin/protoc --plugin=protoc-gen-mypy=$HOME/.local/bin/protoc-gen-mypy --python_out=src/protobuf_generated_python --mypy_out=src/protobuf_generated_python --proto_path=src google_auth.proto"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
# Update README.md
|
||||
|
||||
cmd="perl -i -pe 's%proto(buf|c)([- ])(\d\.)?$OLDVERSION%proto\$1\$2\${3}$VERSION%g' README.md && perl -i -pe 's%(protobuf/releases/tag/v)$OLDVERSION%\${1}$VERSION%g' README.md"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
else
|
||||
echo -e "\nVersion has not changed. Quit"
|
||||
fi
|
||||
else
|
||||
echo -e "\nVersion has not changed. Quit"
|
||||
echo -e "\nIgnore version check. Using existing protoc version.\n"
|
||||
fi
|
||||
|
||||
# Upgrade pip requirements
|
||||
|
||||
cmd="pip install -U pip"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
$PIP --version
|
||||
|
||||
cmd="$PIP install --use-pep517 -U -r requirements.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
if $build_base; then
|
||||
# Upgrade pip requirements
|
||||
cmd="pip install -U pip"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
$PIP --version
|
||||
|
||||
cmd="$PIP install -U -r requirements.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
# Lint
|
||||
|
||||
LINT_OUT_FILE="tests/reports/flake8_results.txt"
|
||||
@@ -360,29 +420,16 @@ if $build_local; then
|
||||
eval "$cmd"
|
||||
|
||||
COVERAGE_OUT_FILE="tests/reports/pytest-coverage.txt"
|
||||
cmd="pytest --cov=extract_otp_secrets_test --junitxml=tests/reports/pytest.xml --cov-report html:tests/reports/html --cov-report=term-missing tests/ | tee $COVERAGE_OUT_FILE"
|
||||
cmd="pytest --cov=extract_otp_secrets --junitxml=tests/reports/pytest.xml --cov-report html:tests/reports/html --cov-report=term-missing tests/ | tee $COVERAGE_OUT_FILE"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
# Pipenv
|
||||
# Update Code Coverage in README.md
|
||||
|
||||
if $run_pipenv; then
|
||||
cmd="$PIP install -U pipenv"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
$PIPENV --version
|
||||
|
||||
cmd="$PIPENV --rm && $PIPENV update && $PIPENV install"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
$PIPENV run python --version
|
||||
|
||||
cmd="$PIPENV run pytest --cov=extract_otp_secrets_test tests/"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
fi
|
||||
# https://github.com/marketplace/actions/pytest-coverage-comment
|
||||
# Coverage-95%25-yellowgreen
|
||||
echo -e "Update code coverage in README.md"
|
||||
TOTAL_COVERAGE=$(cat $COVERAGE_OUT_FILE | grep 'TOTAL' | perl -ne 'print "$&" if /\b(\d{1,3})%/') && perl -i -pe "s/coverage-(\d{1,3}%)25-/coverage-${TOTAL_COVERAGE}25-/" README.md
|
||||
|
||||
# Build wheel
|
||||
|
||||
@@ -391,6 +438,147 @@ if $build_local; then
|
||||
eval "$cmd"
|
||||
fi
|
||||
|
||||
# Pipenv
|
||||
if $run_pipenv; then
|
||||
cmd="$PIP install -U pipenv"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
$PIPENV --version
|
||||
|
||||
cmd="rm Pipfile.lock || echo 'No Pipfile.lock to remove'"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="# $PIPENV --rm || echo 'No virtualenv to remove'"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$PIPENV install --dev"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$PIPENV update --dev"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
$PIPENV run python --version
|
||||
|
||||
# pip -e install
|
||||
|
||||
cmd="$PIPENV run pip install -U -e ."
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$PIPENV run pytest --cov=extract_otp_secrets tests/"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
|
||||
cmd="$PIPENV run extract_otp_secrets example_export.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$PIPENV run extract_otp_secrets - < example_export.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
# Test (needs module)
|
||||
|
||||
cmd="$PIPENV run python src/extract_otp_secrets.py example_export.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$PIPENV run python src/extract_otp_secrets.py - < example_export.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
fi
|
||||
|
||||
# uv
|
||||
|
||||
if $run_uv; then
|
||||
cmd="rm -rf $UVENV || true"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$PIP install -U uv"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
$UV --version
|
||||
|
||||
cmd="rm uv.lock || echo 'No uv.lock to remove'"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$UV venv $UVENV $VERBOSE --python $PYTHON_VERSION --clear"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="source $UV_BIN_PATH/activate"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$UV pip install $VERBOSE -U -r requirements.txt --exclude excludes.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$UV pip install $VERBOSE -U -r requirements-dev.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$UV pip install $VERBOSE -U -e . --exclude excludes.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$UV lock --refresh $VERBOSE"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$UV sync --active $VERBOSE"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$UV pip uninstall $VERBOSE opencv-python"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$UV run --active python --version"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$UV run $VERBOSE --active pytest tests/"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$UV run $VERBOSE --active extract_otp_secrets example_export.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$UV run $VERBOSE --active extract_otp_secrets - < example_export.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
# Test (needs module)
|
||||
|
||||
cmd="$UV run --active python src/extract_otp_secrets.py example_export.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$UV run --active python src/extract_otp_secrets.py example_export.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$UV run --active python src/extract_otp_secrets.py - < example_export.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="deactivate"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
fi
|
||||
|
||||
|
||||
# Build executable
|
||||
if $build_exe; then
|
||||
cmd="LOCAL_GLIBC_VERSION=$(ldd --version | sed '1!d' | sed -E 's/.* ([[:digit:]]+\.[[:digit:]]+)$/\1/')"
|
||||
@@ -398,13 +586,17 @@ if $build_local; then
|
||||
eval "$cmd"
|
||||
echo "local glibc: $LOCAL_GLIBC_VERSION"
|
||||
|
||||
cmd="pyinstaller -y --specpath installer --add-data $HOME/.local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile $clean_flag src/extract_otp_secrets.py"
|
||||
cmd="time pyinstaller -y --specpath installer --add-data $HOME/.local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile $clean_flag src/extract_otp_secrets.py"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="dist/extract_otp_secrets -h"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="dist/extract_otp_secrets --qr ZBAR example_export.png"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
fi
|
||||
|
||||
# Build compiled executable
|
||||
@@ -419,7 +611,7 @@ if $build_local; then
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$PYTHON -m nuitka --enable-plugin=tk-inter --enable-plugin=pyqt5 --include-data-dir=$HOME/.local/__yolo_v3_qr_detector/=__yolo_v3_qr_detector/ --onefile --output-dir=build/nuitka --output-filename=extract_otp_secrets_compiled src/extract_otp_secrets.py"
|
||||
cmd="time $PYTHON -m nuitka --enable-plugin=tk-inter --enable-plugin=pyqt5 --noinclude-default-mode=nofollow --clang --include-data-dir=$HOME/.local/__yolo_v3_qr_detector/=__yolo_v3_qr_detector/ --onefile --output-dir=build/nuitka --output-filename=extract_otp_secrets_compiled src/extract_otp_secrets.py"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
@@ -430,28 +622,24 @@ if $build_local; then
|
||||
cmd="dist/extract_otp_secrets_compiled -h"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="dist/extract_otp_secrets_compiled --qr ZBAR example_export.png"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
fi
|
||||
|
||||
# Generate README.md TOC
|
||||
|
||||
cmd="gfm-toc -s 2 -e 3 -t -o README.md > docs/README_TOC.md"
|
||||
cmd="env -u MSYS_NO_PATHCONV gfm-toc -s 2 -e 3 -t -o README.md > docs/README_TOC.md"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
# Update Code Coverage in README.md
|
||||
|
||||
# https://github.com/marketplace/actions/pytest-coverage-comment
|
||||
# Coverage-95%25-yellowgreen
|
||||
echo -e "Update code coverage in README.md"
|
||||
TOTAL_COVERAGE=$(cat $COVERAGE_OUT_FILE | grep 'TOTAL' | perl -ne 'print "$&" if /\b(\d{1,3})%/') && perl -i -pe "s/coverage-(\d{1,3}%)25-/coverage-${TOTAL_COVERAGE}25-/" README.md
|
||||
|
||||
# create Windows win_file_version_info.txt
|
||||
cmd="VERSION_STR=$(setuptools-git-versioning) VERSION_MAJOR=$(cut -d '.' -f 1 <<< "$(setuptools-git-versioning)") VERSION_MINOR=$(cut -d '.' -f 2 <<< "$(setuptools-git-versioning)") VERSION_PATCH=$(cut -d '.' -f 3 <<< "$(setuptools-git-versioning)") VERSION_BUILD=$(($(git rev-list --count $(git tag | sort -V -r | sed '1!d')..HEAD))) COPYRIGHT_YEARS='2020-2023' envsubst < installer/win_file_version_info_template.txt > build/win_file_version_info.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
# create macOS extract_otp_secrets_macos.spec from extract_otp_secrets_macos_template.spec
|
||||
cmd="VERSION_STR=$(setuptools-git-versioning) COPYRIGHT_YEARS='2020-2023' envsubst < installer/extract_otp_secrets_macos_template.spec > build/extract_otp_secrets_macos.spec"
|
||||
cmd="VERSION_STR=$(setuptools-git-versioning) COPYRIGHT_YEARS='2020-$(date +%Y)' envsubst < installer/extract_otp_secrets_macos_template.spec > build/extract_otp_secrets_macos.spec"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
fi
|
||||
@@ -461,85 +649,85 @@ if $build_docker; then
|
||||
|
||||
if $build_x86_64; then
|
||||
# Build Dockerfile_only_txt (Alpine)
|
||||
cmd="docker build . -t extract_otp_secrets_only_txt -t extract_otp_secrets:only-txt -t extract_otp_secrets:alpine -f docker/Dockerfile_only_txt --pull --build-arg RUN_TESTS=false"
|
||||
cmd="$DOCKER build . --network=host -t extract_otp_secrets_only_txt -t extract_otp_secrets:only-txt -t extract_otp_secrets:alpine -f docker/Dockerfile_only_txt --pull --build-arg RUN_TESTS=false"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="docker run --rm -v \"$(pwd)\":/files:ro extract_otp_secrets_only_txt example_export.txt"
|
||||
cmd="$DOCKER run --network none --rm -v \"$($PWD):/files:ro\" extract_otp_secrets_only_txt example_export.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="docker run --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets_only_txt - < example_export.txt"
|
||||
cmd="$DOCKER run --network none --rm -i -v \"$($PWD):/files:ro\" extract_otp_secrets_only_txt - < example_export.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="docker run --entrypoint /extract/run_pytest.sh --rm -v \"$(pwd)\":/files:ro extract_otp_secrets_only_txt extract_otp_secrets_test.py -k 'not qreader' -vvv --relaxed"
|
||||
cmd="$DOCKER run --network=host --entrypoint '/extract/run_pytest.sh' --rm -v \"$($PWD):/files:ro\" extract_otp_secrets_only_txt extract_otp_secrets_test.py -k 'not qreader' -vvv --relaxed"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
# Build extract_otp_secrets (Debian Bookworm)
|
||||
cmd="docker build . -t extract_otp_secrets -t extract_otp_secrets:bookworm --pull -f docker/Dockerfile --build-arg RUN_TESTS=false"
|
||||
cmd="$DOCKER build . --network=host -t extract_otp_secrets -t extract_otp_secrets:bookworm --pull -f docker/Dockerfile --build-arg RUN_TESTS=false"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="docker run --rm -v \"$(pwd)\":/files:ro extract_otp_secrets example_export.txt"
|
||||
cmd="$DOCKER run --network none --rm -v \"$($PWD):/files:ro\" extract_otp_secrets example_export.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="cat example_export.txt | docker run --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets - -c - > example_output.csv"
|
||||
cmd="cat example_export.txt | $DOCKER run --network none --rm -i -v \"$($PWD):/files:ro\" extract_otp_secrets - -c - > example_output.csv"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="docker run --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets = < example_export.png"
|
||||
cmd="$DOCKER run --network none --rm -i -v \"$($PWD):/files:ro\" extract_otp_secrets = < example_export.png"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="docker run --entrypoint /extract/run_pytest.sh --rm -v \"$(pwd)\":/files:ro extract_otp_secrets"
|
||||
cmd="$DOCKER run --network=host --entrypoint /extract/run_pytest.sh --rm -v \"$($PWD):/files:ro\" extract_otp_secrets"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
# Build extract_otp_secrets (Debian Buster)
|
||||
cmd="docker build . -t extract_otp_secrets:buster -t extract_otp_secrets:buster-x86_64 --pull -f docker/Dockerfile --build-arg RUN_TESTS=false --build-arg BASE_IMAGE=python:3.11-slim-buster"
|
||||
# Build extract_otp_secrets (Debian Bullseye)
|
||||
cmd="$DOCKER build . --network=host -t extract_otp_secrets:bullseye -t extract_otp_secrets:bullseye-x86_64 --pull -f docker/Dockerfile --build-arg RUN_TESTS=false --build-arg BASE_IMAGE=python:3.13-slim-bullseye"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="docker run --rm -v \"$(pwd)\":/files:ro extract_otp_secrets:buster example_export.txt"
|
||||
cmd="$DOCKER run --network none --rm -v \"$($PWD):/files:ro\" extract_otp_secrets:bullseye example_export.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="cat example_export.txt | docker run --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets:buster - -c - > example_output.csv"
|
||||
cmd="cat example_export.txt | $DOCKER run --network none --rm -i -v \"$($PWD):/files:ro\" extract_otp_secrets:bullseye - -c - > example_output.csv"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="docker run --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets:buster = < example_export.png"
|
||||
cmd="$DOCKER run --network none --rm -i -v \"$($PWD):/files:ro\" extract_otp_secrets:bullseye = < example_export.png"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="docker run --entrypoint /extract/run_pytest.sh --rm -v \"$(pwd)\":/files:ro extract_otp_secrets:buster"
|
||||
cmd="$DOCKER run --network=host --entrypoint /extract/run_pytest.sh --rm -v \"$($PWD):/files:ro\" extract_otp_secrets:bullseye"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
# Build executable from Docker latest
|
||||
# sed "1!d" is workaround for head -n 1 since it head procduces exit code != 0
|
||||
BOOKWORM_GLIBC_VERSION=$(docker run --entrypoint /bin/bash --rm extract_otp_secrets -c 'ldd --version | sed "1!d" | sed -E "s/.* ([[:digit:]]+\.[[:digit:]]+)$/\1/"')
|
||||
BOOKWORM_GLIBC_VERSION=$($DOCKER run --network none --entrypoint /bin/bash --rm extract_otp_secrets -c 'ldd --version | sed "1!d" | sed -E "s/.* ([[:digit:]]+\.[[:digit:]]+)$/\1/"')
|
||||
echo "Bookworm glibc: $BOOKWORM_GLIBC_VERSION"
|
||||
fi
|
||||
|
||||
if $build_arm; then
|
||||
# build linux/arm64
|
||||
cmd="docker run --pull always --rm --privileged multiarch/qemu-user-static --reset -p yes"
|
||||
cmd="$DOCKER run --network none --pull always --rm --privileged multiarch/qemu-user-static --reset -p yes"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
# Build extract_otp_secrets (Debian Buster)
|
||||
cmd="docker buildx build --platform=linux/arm64 . -t extract_otp_secrets:buster-arm64 --pull -f docker/Dockerfile --build-arg RUN_TESTS=false --build-arg BASE_IMAGE=python:3.11-slim-buster"
|
||||
# Build extract_otp_secrets (Debian Bullseye)
|
||||
cmd="$DOCKER buildx build --platform=linux/arm64 . -t extract_otp_secrets:bullseye-arm64 --pull -f docker/Dockerfile --build-arg RUN_TESTS=false --build-arg BASE_IMAGE=python:3.13-slim-bullseye"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
fi
|
||||
|
||||
if $build_exe; then
|
||||
if $build_x86_64; then
|
||||
cmd="docker run --platform linux/amd64 --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets -c 'apt-get update && apt-get -y install binutils && pip install -U pip && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller -y --specpath installer --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_linux_x86_64_bookworm --distpath /files/dist/ /files/src/extract_otp_secrets.py'"
|
||||
cmd="$DOCKER run --platform linux/amd64 --network=host --entrypoint /bin/bash --rm -v \"$($PWD):/files\" -w /files extract_otp_secrets -c 'apt-get update && apt-get -y install binutils && pip install -U pip && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller -y --specpath installer --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_linux_x86_64_bookworm --distpath /files/dist/ /files/src/extract_otp_secrets.py'"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
@@ -547,26 +735,34 @@ if $build_docker; then
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
# Build executable from Docker buster
|
||||
BUSTER_GLIBC_VERSION=$(docker run --entrypoint /bin/bash --rm extract_otp_secrets:buster -c 'ldd --version | sed "1!d" | sed -E "s/.* ([[:digit:]]+\.[[:digit:]]+)$/\1/"')
|
||||
echo "Bookworm glibc: $BUSTER_GLIBC_VERSION"
|
||||
cmd="dist/extract_otp_secrets_linux_x86_64_bookworm --qr ZBAR example_export.png || echo 'Could not run exe; see error message above'"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="docker run --platform linux/amd64 --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets:buster -c 'apt-get update && apt-get -y install binutils && pip install -U pip && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller -y --specpath installer --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_linux_x86_64 --distpath /files/dist/ /files/src/extract_otp_secrets.py'"
|
||||
# Build executable from Docker bullseye
|
||||
BULLSEYE_GLIBC_VERSION=$($DOCKER run --network none --entrypoint /bin/bash --rm extract_otp_secrets:bullseye -c 'ldd --version | sed "1!d" | sed -E "s/.* ([[:digit:]]+\.[[:digit:]]+)$/\1/"')
|
||||
echo "Bullseye glibc: $BULLSEYE_GLIBC_VERSION"
|
||||
|
||||
cmd="$DOCKER run --platform linux/amd64 --network=host --entrypoint /bin/bash --rm -v \"$($PWD):/files\" -w /files extract_otp_secrets:bullseye -c 'apt-get update && apt-get -y install binutils && pip install -U pip && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller -y --specpath installer --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_linux_x86_64 --distpath /files/dist/ /files/src/extract_otp_secrets.py'"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="dist/extract_otp_secrets_linux_x86_64 -h"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="dist/extract_otp_secrets_linux_x86_64 --qr ZBAR example_export.png"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
fi
|
||||
|
||||
if $build_arm; then
|
||||
# build linux/arm64
|
||||
cmd="docker run --platform linux/arm64 --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets:buster-arm64 -c 'apt-get update && apt-get -y install binutils && pip install -U pip && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller --specpath installer -y --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_linux_arm64 --distpath /files/dist/ /files/src/extract_otp_secrets.py'"
|
||||
cmd="$DOCKER run --platform linux/arm64 --network=host --entrypoint /bin/bash --rm -v \"$($PWD):/files\" -w /files extract_otp_secrets:bullseye-arm64 -c 'apt-get update && apt-get -y install binutils && pip install -U pip && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller --specpath installer -y --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_linux_arm64 --distpath /files/dist/ /files/src/extract_otp_secrets.py'"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="PLATFORM='linux/arm64' && EXE='dist/extract_otp_secrets_linux_arm64' && docker run --platform \"\$PLATFORM\" --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets:buster-arm64 -c \"\$EXE -V && \$EXE -h && \$EXE example_export.png && \$EXE - < example_export.txt && \$EXE --qr ZBAR example_export.png && \$EXE --qr QREADER example_export.png && \$EXE --qr QREADER_DEEP example_export.png && \$EXE --qr CV2 example_export.png && \$EXE --qr CV2_WECHAT example_export.png\""
|
||||
cmd="PLATFORM='linux/arm64' && EXE='dist/extract_otp_secrets_linux_arm64' && $DOCKER run --platform \"\$PLATFORM\" --entrypoint /bin/bash --rm -v \"$($PWD):/files\" -w /files extract_otp_secrets:bullseye-arm64 -c \"\$EXE -V && \$EXE -h && \$EXE example_export.png && \$EXE - < example_export.txt && \$EXE --qr ZBAR example_export.png && \$EXE --qr QREADER example_export.png && \$EXE --qr QREADER_DEEP example_export.png && \$EXE --qr CV2 example_export.png && \$EXE --qr CV2_WECHAT example_export.png\""
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
fi
|
||||
@@ -574,7 +770,7 @@ if $build_docker; then
|
||||
|
||||
if $build_nuitka_exe; then
|
||||
if $build_x86_64; then
|
||||
cmd="docker run --platform linux/amd64 --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets -c 'apt-get update && apt-get -y install binutils build-essential patchelf && pip install -U pip && pip install -U -r /files/requirements.txt && pip install nuitka pyqt5 && PYTHONHASHSEED=31 && python -m nuitka --enable-plugin=tk-inter --enable-plugin=pyqt5 --include-data-dir=/usr/local/__yolo_v3_qr_detector/=__yolo_v3_qr_detector/ --onefile --output-dir=/files/build/docker/nuitka --output-filename=extract_otp_secrets_linux_x86_64_bookworm_compiled /files/src/extract_otp_secrets.py'"
|
||||
cmd="$DOCKER run --platform linux/amd64 --network=host --entrypoint /bin/bash --rm -v \"$($PWD):/files\" -w /files extract_otp_secrets -c 'apt-get update && apt-get -y install binutils build-essential patchelf && pip install -U pip && pip install -U -r /files/requirements.txt && pip install nuitka pyqt5 && PYTHONHASHSEED=31 && python -m nuitka --enable-plugin=tk-inter --enable-plugin=pyqt5 --include-data-dir=/usr/local/__yolo_v3_qr_detector/=__yolo_v3_qr_detector/ --onefile --output-dir=/files/build/docker/nuitka --output-filename=extract_otp_secrets_linux_x86_64_bookworm_compiled /files/src/extract_otp_secrets.py'"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
@@ -582,34 +778,42 @@ if $build_docker; then
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="build/docker/nuitka/extract_otp_secrets_linux_x86_64_bookworm_compiled --qr ZBAR example_export.png"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="cp build/docker/nuitka/extract_otp_secrets_linux_x86_64_bookworm_compiled dist/"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
# Build executable from Docker buster
|
||||
BUSTER_GLIBC_VERSION=$(docker run --entrypoint /bin/bash --rm extract_otp_secrets:buster -c 'ldd --version | sed "1!d" | sed -E "s/.* ([[:digit:]]+\.[[:digit:]]+)$/\1/"')
|
||||
echo "Bookworm glibc: $BUSTER_GLIBC_VERSION"
|
||||
# Build executable from Docker bullseye
|
||||
BULLSEYE_GLIBC_VERSION=$($DOCKER run --network none --entrypoint /bin/bash --rm extract_otp_secrets:bullseye -c 'ldd --version | sed "1!d" | sed -E "s/.* ([[:digit:]]+\.[[:digit:]]+)$/\1/"')
|
||||
echo "Bookworm glibc: $BULLSEYE_GLIBC_VERSION"
|
||||
|
||||
cmd="docker run --platform linux/amd64 --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets:buster -c 'apt-get update && apt-get -y install binutils build-essential patchelf && pip install -U pip && pip install -U -r /files/requirements.txt && pip install nuitka pyqt5 && PYTHONHASHSEED=31 && python -m nuitka --enable-plugin=tk-inter --enable-plugin=pyqt5 --include-data-dir=/usr/local/__yolo_v3_qr_detector/=__yolo_v3_qr_detector/ --onefile --output-dir=/files/build/docker/nuitka --output-filename=extract_otp_secrets_linux_x86_64_buster_compiled /files/src/extract_otp_secrets.py'"
|
||||
cmd="$DOCKER run --platform linux/amd64 --network=host --entrypoint /bin/bash --rm -v \"$($PWD):/files\" -w /files extract_otp_secrets:bullseye -c 'apt-get update && apt-get -y install binutils build-essential patchelf && pip install -U pip && pip install -U -r /files/requirements.txt && pip install nuitka pyqt5 && PYTHONHASHSEED=31 && python -m nuitka --enable-plugin=tk-inter --enable-plugin=pyqt5 --include-data-dir=/usr/local/__yolo_v3_qr_detector/=__yolo_v3_qr_detector/ --onefile --output-dir=/files/build/docker/nuitka --output-filename=extract_otp_secrets_linux_x86_64_bullseye_compiled /files/src/extract_otp_secrets.py'"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="build/docker/nuitka/extract_otp_secrets_linux_x86_64_buster_compiled -h"
|
||||
cmd="build/docker/nuitka/extract_otp_secrets_linux_x86_64_bullseye_compiled -h"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="cp build/docker/nuitka/extract_otp_secrets_linux_x86_64_buster_compiled dist/"
|
||||
cmd="build/docker/nuitka/extract_otp_secrets_linux_x86_64_bullseye_compiled --qr ZBAR example_export.png"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="cp build/docker/nuitka/extract_otp_secrets_linux_x86_64_bullseye_compiled dist/"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
fi
|
||||
|
||||
if $build_arm; then
|
||||
# build linux/arm64
|
||||
cmd="docker run --platform linux/arm64 --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets:buster-arm64 -c 'apt-get update && apt-get -y install binutils build-essential patchelf qt5-default && pip install -U pip && pip install -U -r /files/requirements.txt && pip install nuitka pyqt5 && PYTHONHASHSEED=31 && python -m nuitka --enable-plugin=tk-inter --enable-plugin=pyqt5 --include-data-dir=/usr/local/__yolo_v3_qr_detector/=__yolo_v3_qr_detector/ --onefile --output-dir=/files/build/docker/nuitka --output-filename=extract_otp_secrets_linux_arm64_compiled /files/src/extract_otp_secrets.py'"
|
||||
cmd="$DOCKER run --platform linux/arm64 --network=host --entrypoint /bin/bash --rm -v \"$($PWD):/files\" -w /files extract_otp_secrets:bullseye-arm64 -c 'apt-get update && apt-get -y install binutils build-essential patchelf qt5-default && pip install -U pip && pip install -U -r /files/requirements.txt && pip install nuitka pyqt5 && PYTHONHASHSEED=31 && python -m nuitka --enable-plugin=tk-inter --enable-plugin=pyqt5 --include-data-dir=/usr/local/__yolo_v3_qr_detector/=__yolo_v3_qr_detector/ --onefile --output-dir=/files/build/docker/nuitka --output-filename=extract_otp_secrets_linux_arm64_compiled /files/src/extract_otp_secrets.py'"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="PLATFORM='linux/arm64' && EXE='build/docker/nuitka/extract_otp_secrets_linux_arm64_compiled' && docker run --platform \"\$PLATFORM\" --entrypoint /bin/bash --rm -v \"$(pwd)\":/files -w /files extract_otp_secrets:buster-arm64 -c \"\$EXE -V && \$EXE -h && \$EXE example_export.png && \$EXE - < example_export.txt && \$EXE --qr ZBAR example_export.png && \$EXE --qr QREADER example_export.png && \$EXE --qr QREADER_DEEP example_export.png && \$EXE --qr CV2 example_export.png && \$EXE --qr CV2_WECHAT example_export.png\""
|
||||
cmd="PLATFORM='linux/arm64' && EXE='build/docker/nuitka/extract_otp_secrets_linux_arm64_compiled' && $DOCKER run --platform \"\$PLATFORM\" --entrypoint /bin/bash --rm -v \"$($PWD):/files\" -w /files extract_otp_secrets:bullseye-arm64 -c \"\$EXE -V && \$EXE -h && \$EXE example_export.png && \$EXE - < example_export.txt && \$EXE --qr ZBAR example_export.png && \$EXE --qr QREADER example_export.png && \$EXE --qr QREADER_DEEP example_export.png && \$EXE --qr CV2 example_export.png && \$EXE --qr CV2_WECHAT example_export.png\""
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
|
||||
@@ -621,7 +825,7 @@ if $build_docker; then
|
||||
|
||||
# Run GUI from Docker
|
||||
if $build_x86_64 && $run_gui; then
|
||||
cmd="docker run --rm -v "$(pwd)":/files:ro --device=\"/dev/video0:/dev/video0\" --env=\"DISPLAY\" -v /tmp/.X11-unix:/tmp/.X11-unix:ro extract_otp_secrets &"
|
||||
cmd="$DOCKER run --network none --rm -v "$($PWD):/files:ro" --device=\"/dev/video0:/dev/video0\" --env=\"DISPLAY\" -v /tmp/.X11-unix:/tmp/.X11-unix:ro extract_otp_secrets &"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
fi
|
||||
@@ -633,12 +837,14 @@ if $run_gui; then
|
||||
eval "$cmd"
|
||||
fi
|
||||
|
||||
line=$(printf '#%.0s' $(eval echo {1..$(( ($COLUMNS - 10) / 2))}))
|
||||
echo -e "\n${blueBold}$line RESULTS $line${reset}"
|
||||
if $build_base; then
|
||||
line=$(printf '#%.0s' $(eval echo {1..$(( ($COLUMNS - 10) / 2))}))
|
||||
echo -e "\n${blueBold}$line RESULTS $line${reset}"
|
||||
|
||||
cmd="cat $TYPE_CHECK_OUT_FILE $LINT_OUT_FILE $COVERAGE_OUT_FILE"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
cmd="cat $TYPE_CHECK_OUT_FILE $LINT_OUT_FILE $COVERAGE_OUT_FILE"
|
||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||
eval "$cmd"
|
||||
fi
|
||||
|
||||
line=$(printf '#%.0s' $(eval echo {1..$(( ($COLUMNS - 10) / 2))}))
|
||||
echo -e "\n${greenBold}$line SUCCESS $line${reset}"
|
||||
|
||||
122
devenv.lock
Normal file
122
devenv.lock
Normal file
@@ -0,0 +1,122 @@
|
||||
{
|
||||
"nodes": {
|
||||
"devenv": {
|
||||
"locked": {
|
||||
"dir": "src/modules",
|
||||
"lastModified": 1726050924,
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "cf471f691c3765ed431199f61b8bd70530bc4305",
|
||||
"treeHash": "04a5d566820f8fb1955c7c49ccbd3ff95d9129d7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"dir": "src/modules",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"treeHash": "2addb7b71a20a25ea74feeaf5c2f6a6b30898ecb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"pre-commit-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
"treeHash": "ca14199cabdfe1a06a7b1654c76ed49100a689f9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1716977621,
|
||||
"owner": "cachix",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"rev": "4267e705586473d3e5c8d50299e71503f16a6fb6",
|
||||
"treeHash": "6d9f1f7ca0faf1bc2eeb397c78a49623260d3412",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"ref": "rolling",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1725826545,
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "f4c846aee8e1e29062aa8514d5e0ab270f4ec2f9",
|
||||
"treeHash": "8fc49deaed3f2728a7147c38163cc468a117570a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1725513492,
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "7570de7b9b504cfe92025dd1be797bf546f66528",
|
||||
"treeHash": "4b46d77870afecd8f642541cb4f4927326343b59",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"devenv": "devenv",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
24
devenv.nix
Normal file
24
devenv.nix
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
cachix.enable = false;
|
||||
|
||||
languages.python = {
|
||||
enable = true;
|
||||
venv = {
|
||||
enable = true;
|
||||
requirements = ./requirements.txt;
|
||||
};
|
||||
};
|
||||
|
||||
packages = [
|
||||
pkgs.git
|
||||
pkgs.zbar
|
||||
];
|
||||
}
|
||||
14
devenv.yaml
Normal file
14
devenv.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
inputs:
|
||||
nixpkgs:
|
||||
url: github:cachix/devenv-nixpkgs/rolling
|
||||
|
||||
# If you're using non-OSS software, you can set allowUnfree to true.
|
||||
# allowUnfree: true
|
||||
|
||||
# If you're willing to use a package that's vulnerable
|
||||
# permittedInsecurePackages:
|
||||
# - "openssl-1.1.1w"
|
||||
|
||||
# If you have more than one devenv you can merge them
|
||||
#imports:
|
||||
# - ./backend
|
||||
@@ -1,5 +1,5 @@
|
||||
# --build-arg BASE_IMAGE=python:3.11-slim-buster
|
||||
ARG BASE_IMAGE=python:3.11-slim-bookworm
|
||||
ARG BASE_IMAGE=python:3.14-slim-trixie
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
# https://docs.docker.com/engine/reference/builder/
|
||||
@@ -16,7 +16,8 @@ COPY requirements*.txt src/ run_pytest.sh pytest.ini tests/ example_*.txt exampl
|
||||
|
||||
ARG RUN_TESTS=true
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
RUN uname -a \
|
||||
&& apt-get update && apt-get install -y --no-install-recommends \
|
||||
libgl1 \
|
||||
libglib2.0-0 \
|
||||
libsm6 \
|
||||
@@ -31,6 +32,6 @@ WORKDIR /files
|
||||
|
||||
ENTRYPOINT ["python", "/extract/extract_otp_secrets.py"]
|
||||
|
||||
LABEL org.opencontainers.image.source https://github.com/scito/extract_otp_secrets
|
||||
LABEL org.opencontainers.image.license GPL-3.0+
|
||||
LABEL org.opencontainers.image.source=https://github.com/scito/extract_otp_secrets
|
||||
LABEL org.opencontainers.image.license=GPL-3.0+
|
||||
LABEL maintainer="Scito https://scito.ch, https://github.com/scito"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ARG BASE_IMAGE=python:3.11-alpine
|
||||
ARG BASE_IMAGE=python:3.14-alpine
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
# https://docs.docker.com/engine/reference/builder/
|
||||
@@ -17,7 +17,8 @@ COPY requirements*.txt src/ run_pytest.sh pytest.ini tests/ example_*.txt exampl
|
||||
|
||||
ARG RUN_TESTS=true
|
||||
|
||||
RUN apk add --no-cache \
|
||||
RUN uname -a \
|
||||
&& apk add --no-cache \
|
||||
jpeg \
|
||||
zlib \
|
||||
&& echo "Arch: $(apk --print-arch)" \
|
||||
@@ -43,6 +44,6 @@ WORKDIR /files
|
||||
|
||||
ENTRYPOINT ["python", "/extract/extract_otp_secrets.py"]
|
||||
|
||||
LABEL org.opencontainers.image.source https://github.com/scito/extract_otp_secrets
|
||||
LABEL org.opencontainers.image.license GPL-3.0+
|
||||
LABEL org.opencontainers.image.source=https://github.com/scito/extract_otp_secrets
|
||||
LABEL org.opencontainers.image.license=GPL-3.0+
|
||||
LABEL maintainer="Scito https://scito.ch, https://github.com/scito"
|
||||
|
||||
@@ -13,7 +13,7 @@ Generate from file: README.md
|
||||
- [Installation of optional shared system libraries (recommended)](#installation-of-optional-shared-system-libraries-recommended)
|
||||
- [Program help: arguments and options](#program-help-arguments-and-options)
|
||||
- [Examples](#examples)
|
||||
- [Printing otp secrets form text file](#printing-otp-secrets-form-text-file)
|
||||
- [Printing otp secrets from text file](#printing-otp-secrets-from-text-file)
|
||||
- [Printing otp secrets from image file](#printing-otp-secrets-from-image-file)
|
||||
- [Writing otp secrets to csv file](#writing-otp-secrets-to-csv-file)
|
||||
- [Writing otp secrets to json file](#writing-otp-secrets-to-json-file)
|
||||
@@ -33,7 +33,8 @@ Generate from file: README.md
|
||||
- [pipenv](#pipenv)
|
||||
- [Visual Studio Code Remote - Containers / VSCode devcontainer](#visual-studio-code-remote---containers--vscode-devcontainer)
|
||||
- [venv](#venv)
|
||||
- [devbox](#devbox)
|
||||
- [devbox 1](#devbox-1)
|
||||
- [devbox 2](#devbox-2)
|
||||
- [docker](#docker)
|
||||
- [More docker examples](#more-docker-examples)
|
||||
- [Tests](#tests)
|
||||
@@ -52,5 +53,6 @@ Generate from file: README.md
|
||||
- [Problems and Troubleshooting](#problems-and-troubleshooting)
|
||||
- [Windows error message](#windows-error-message)
|
||||
- [Related projects](#related-projects)
|
||||
- [Third party documentation](#third-party-documentation)
|
||||
|
||||
Table of contents generated.
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
Title,User Name,HmacOtp-Secret-Base32,HmacOtp-Counter,Group
|
||||
,hotp demo,7KSQL2JTUDIS5EF65KLMRQIIGY,4,OTP/HOTP
|
||||
Title,User Name,HmacOtp-Secret-Base32,HmacOtp-Counter,Group
|
||||
,hotp demo,7KSQL2JTUDIS5EF65KLMRQIIGY,4,OTP/HOTP
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
Title,User Name,TimeOtp-Secret-Base32,Group
|
||||
raspberrypi,pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,OTP/TOTP
|
||||
,pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,OTP/TOTP
|
||||
,pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,OTP/TOTP
|
||||
raspberrypi,pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,OTP/TOTP
|
||||
,encoding: ¿äÄéÉ? (demo),7KSQL2JTUDIS5EF65KLMRQIIGY,OTP/TOTP
|
||||
Title,User Name,TimeOtp-Secret-Base32,Group
|
||||
raspberrypi,pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,OTP/TOTP
|
||||
,pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,OTP/TOTP
|
||||
,pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,OTP/TOTP
|
||||
raspberrypi,pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,OTP/TOTP
|
||||
,encoding: ¿äÄéÉ? (demo),7KSQL2JTUDIS5EF65KLMRQIIGY,OTP/TOTP
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
name,secret,issuer,type,counter,url
|
||||
pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,raspberrypi,totp,,otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
|
||||
pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,,totp,,otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,,totp,,otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,raspberrypi,totp,,otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
|
||||
hotp demo,7KSQL2JTUDIS5EF65KLMRQIIGY,,hotp,4,otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4
|
||||
encoding: ¿äÄéÉ? (demo),7KSQL2JTUDIS5EF65KLMRQIIGY,,totp,,otpauth://totp/encoding%3A%20%C2%BF%C3%A4%C3%84%C3%A9%C3%89%3F%20%28demo%29?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
name,secret,issuer,type,counter,url
|
||||
pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,raspberrypi,totp,,otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
|
||||
pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,,totp,,otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,,totp,,otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,raspberrypi,totp,,otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
|
||||
hotp demo,7KSQL2JTUDIS5EF65KLMRQIIGY,,hotp,4,otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4
|
||||
encoding: ¿äÄéÉ? (demo),7KSQL2JTUDIS5EF65KLMRQIIGY,,totp,,otpauth://totp/encoding%3A%20%C2%BF%C3%A4%C3%84%C3%A9%C3%89%3F%20%28demo%29?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
|
||||
|
1
excludes.txt
Normal file
1
excludes.txt
Normal file
@@ -0,0 +1 @@
|
||||
opencv-python
|
||||
@@ -3,8 +3,7 @@ requires = [
|
||||
"pip",
|
||||
"nuitka",
|
||||
# https://setuptools-git-versioning.readthedocs.io/en/latest/differences.html
|
||||
"setuptools>=64.0.0",
|
||||
"setuptools-git-versioning",
|
||||
"setuptools-git-versioning",
|
||||
"wheel>=0.37.0",
|
||||
]
|
||||
build-backend = "setuptools.build_meta"
|
||||
@@ -21,11 +20,11 @@ classifiers = [
|
||||
"Topic :: Utilities",
|
||||
"Topic :: Security",
|
||||
"Topic :: Multimedia :: Graphics :: Capture :: Digital Camera",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"Intended Audience :: Developers",
|
||||
"Intended Audience :: System Administrators",
|
||||
@@ -40,10 +39,11 @@ classifiers = [
|
||||
]
|
||||
dependencies = [
|
||||
"colorama>=0.4.6",
|
||||
"opencv-contrib-python; sys_platform != 'darwin'",
|
||||
"opencv-contrib-python<=4.7.0; sys_platform == 'darwin'",
|
||||
"opencv-contrib-python>=4.13.0",
|
||||
"numpy>=2.2,<2.3 ; python_version >= '3.10' and python_version < '3.11'",
|
||||
"numpy>=2.4.1,<3.0 ; python_version >= '3.11'",
|
||||
"Pillow",
|
||||
"protobuf",
|
||||
"protobuf>=7.34.0",
|
||||
"pyzbar",
|
||||
"qrcode",
|
||||
"qreader<2.0.0",
|
||||
@@ -55,7 +55,7 @@ license = {text = "GNU General Public License v3 (GPLv3)"}
|
||||
readme = "README.md"
|
||||
authors = [{name = "scito", email = "info@scito.ch"}]
|
||||
maintainers = [{name = "scito", email = "info@scito.ch"}]
|
||||
requires-python = ">=3.7, <4"
|
||||
requires-python = ">=3.10, <4"
|
||||
scripts = {extract_otp_secrets = "extract_otp_secrets:sys_main"}
|
||||
urls = {Project-URL = "https://github.com/scito/extract_otp_secrets", Bug-Reports = "https://github.com/scito/extract_otp_secrets/issues", Source = "https://github.com/scito/extract_otp_secrets"}
|
||||
|
||||
@@ -71,5 +71,7 @@ enabled = true
|
||||
|
||||
# https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure%3E
|
||||
# https://docs.pytest.org/en/7.1.x/explanation/goodpractices.html#which-import-mode
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
addopts = [ "--import-mode=importlib", ]
|
||||
testpaths = [ "tests", ]
|
||||
@@ -1,10 +1,9 @@
|
||||
colorama>=0.4.6
|
||||
importlib_metadata; python_version<='3.7'
|
||||
opencv-contrib-python; sys_platform != 'darwin'
|
||||
opencv-contrib-python<=4.7.0; sys_platform == 'darwin'
|
||||
opencv-contrib-python>=4.13.0
|
||||
numpy>=2.2,<2.3 ; python_version >= "3.10" and python_version < "3.11"
|
||||
numpy>=2.4.1,<3.0 ; python_version >= "3.11"
|
||||
Pillow
|
||||
protobuf
|
||||
protobuf>=7.34.0
|
||||
pyzbar
|
||||
qrcode
|
||||
qreader<2.0.0
|
||||
typing_extensions; python_version<='3.7'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = extract_otp_secrets
|
||||
|
||||
[options]
|
||||
python_requires = >=3.7, <4
|
||||
python_requires = >=3.10, <4
|
||||
py_modules = extract_otp_secrets, protobuf_generated_python.google_auth_pb2
|
||||
package_dir =
|
||||
=src
|
||||
|
||||
@@ -36,6 +36,7 @@ import argparse
|
||||
import base64
|
||||
import csv
|
||||
import fileinput
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
@@ -45,10 +46,10 @@ import urllib.parse as urlparse
|
||||
from enum import Enum, IntEnum
|
||||
from importlib.metadata import PackageNotFoundError, version
|
||||
from typing import (Any, Final, List, Optional, Sequence, TextIO, Tuple,
|
||||
TypedDict, Union)
|
||||
TypedDict)
|
||||
|
||||
import colorama
|
||||
from qrcode import QRCode # type: ignore
|
||||
from qrcode import QRCode
|
||||
|
||||
import protobuf_generated_python.google_auth_pb2 as pb
|
||||
|
||||
@@ -59,7 +60,8 @@ headless: bool = False
|
||||
|
||||
try:
|
||||
import cv2
|
||||
import numpy as np # TODO use numpy types if available
|
||||
import cv2.typing
|
||||
import numpy as np
|
||||
|
||||
try:
|
||||
import tkinter
|
||||
@@ -83,9 +85,8 @@ Exception: {e}\n""", file=sys.stderr)
|
||||
raise e
|
||||
|
||||
# Types
|
||||
# workaround for PYTHON <= 3.9: Final[tuple[int]]
|
||||
ColorBGR = Tuple[int, int, int] # RGB Color specified as Blue, Green, Red
|
||||
Point = Tuple[int, int]
|
||||
ColorBGR = tuple[int, int, int] # RGB Color specified as Blue, Green, Red
|
||||
Point = tuple[int, int]
|
||||
|
||||
# CV2 camera capture constants
|
||||
FONT: Final[int] = cv2.FONT_HERSHEY_PLAIN
|
||||
@@ -118,17 +119,12 @@ except ImportError as e:
|
||||
if debug_mode:
|
||||
raise e
|
||||
|
||||
# Workaround for PYTHON <= 3.9: Generally Union[int, None] used instead of int | None
|
||||
|
||||
# Types
|
||||
Args = argparse.Namespace
|
||||
OtpUrl = str
|
||||
# Workaround for PYTHON <= 3.9: Otp = TypedDict('Otp', {'name': str, 'secret': str, 'issuer': str, 'type': str, 'counter': int | None, 'url': OtpUrl})
|
||||
Otp = TypedDict('Otp', {'name': str, 'secret': str, 'issuer': str, 'type': str, 'counter': Union[int, None], 'url': OtpUrl})
|
||||
# workaround for PYTHON <= 3.9: Otps = list[Otp]
|
||||
Otps = List[Otp]
|
||||
# workaround for PYTHON <= 3.9: OtpUrls = list[OtpUrl]
|
||||
OtpUrls = List[OtpUrl]
|
||||
Otp = TypedDict('Otp', {'name': str, 'secret': str, 'issuer': str, 'type': str, 'counter': int | None, 'url': OtpUrl})
|
||||
Otps = list[Otp]
|
||||
OtpUrls = list[OtpUrl]
|
||||
|
||||
QRMode = Enum('QRMode', ['ZBAR', 'QREADER', 'QREADER_DEEP', 'CV2', 'CV2_WECHAT'], start=0)
|
||||
LogLevel = IntEnum('LogLevel', ['QUIET', 'NORMAL', 'VERBOSE', 'MORE_VERBOSE', 'DEBUG'], start=-1)
|
||||
@@ -187,10 +183,10 @@ def main(sys_args: list[str]) -> None:
|
||||
write_keepass_csv(args.keepass, otps)
|
||||
write_json(args.json, otps)
|
||||
write_txt(args.txt, otps, True)
|
||||
write_urls(args.urls, otps)
|
||||
|
||||
|
||||
# workaround for PYTHON <= 3.9 use: pb.MigrationPayload | None
|
||||
def get_payload_from_otp_url(otp_url: str, i: int, source: str) -> Optional[pb.MigrationPayload]:
|
||||
def get_payload_from_otp_url(otp_url: str, i: int, source: str) -> pb.MigrationPayload | None:
|
||||
'''Extracts the otp migration payload from an otp url. This function is the core of the this appliation.'''
|
||||
if not is_opt_url(otp_url, source):
|
||||
return None
|
||||
@@ -279,10 +275,11 @@ def parse_args(sys_args: list[str]) -> Args:
|
||||
epilog=example_text)
|
||||
arg_parser.add_argument('infile', help="""a) file or - for stdin with 'otpauth-migration://...' URLs separated by newlines, lines starting with # are ignored;
|
||||
b) image file containing a QR code or = for stdin for an image containing a QR code""", nargs='*' if cv2_available else '+')
|
||||
arg_parser.add_argument('--csv', '-c', help='export csv file or - for stdout', metavar=('FILE'))
|
||||
arg_parser.add_argument('--csv', '-c', help='export csv file, or - for stdout', metavar=('FILE'))
|
||||
arg_parser.add_argument('--keepass', '-k', help='export totp/hotp csv file(s) for KeePass, - for stdout', metavar=('FILE'))
|
||||
arg_parser.add_argument('--json', '-j', help='export json file or - for stdout', metavar=('FILE'))
|
||||
arg_parser.add_argument('--txt', '-t', help='export txt file or - for stdout', metavar=('FILE'))
|
||||
arg_parser.add_argument('--urls', '-u', help='export file with list of otpauth urls, or - for stdout', metavar=('FILE'))
|
||||
arg_parser.add_argument('--printqr', '-p', help='print QR code(s) as text to the terminal', action='store_true')
|
||||
arg_parser.add_argument('--saveqr', '-s', help='save QR code(s) as images to directory', metavar=('DIR'))
|
||||
if cv2_available:
|
||||
@@ -300,17 +297,18 @@ b) image file containing a QR code or = for stdin for an image containing a QR c
|
||||
output_group.add_argument('-q', '--quiet', help='no stdout output, except output set by -', action='store_true')
|
||||
args = arg_parser.parse_args(sys_args)
|
||||
colored = not args.no_color
|
||||
if args.csv == '-' or args.json == '-' or args.keepass == '-' or args.txt == '-':
|
||||
if args.csv == '-' or args.json == '-' or args.keepass == '-' or args.txt == '-' or args.urls == '-':
|
||||
args.quiet = args.q = True
|
||||
|
||||
verbose = args.verbose if args.verbose else LogLevel.NORMAL
|
||||
if args.debug:
|
||||
verbose = LogLevel.DEBUG
|
||||
log_debug('Debug mode start')
|
||||
log_debug(args)
|
||||
quiet = True if args.quiet else False
|
||||
if verbose: print(f"QReader installed: {cv2_available}")
|
||||
if cv2_available:
|
||||
if verbose >= LogLevel.VERBOSE: print(f"CV2 version: {cv2.__version__}") # type: ignore # cv2.__version__ is not available
|
||||
if verbose >= LogLevel.VERBOSE: print(f"CV2 version: {cv2.__version__}")
|
||||
if verbose: print(f"QR reading mode: {args.qr}\n")
|
||||
|
||||
return args
|
||||
@@ -375,14 +373,22 @@ def extract_otps_from_camera(args: Args) -> Otps:
|
||||
qr_mode = next_qr_mode(qr_mode)
|
||||
continue
|
||||
|
||||
cv2_print_text(img, f"Mode: {qr_mode.name} (Hit SPACE to change)", 0, TextPosition.LEFT, FONT_COLOR, 20)
|
||||
cv2_print_text(img, "Press ESC to quit", 1, TextPosition.LEFT, FONT_COLOR, 17)
|
||||
cv2_print_text(img, "Press C/J/K/T to save as csv/json/keepass/txt file", 2, TextPosition.LEFT, FONT_COLOR, None)
|
||||
try:
|
||||
cv2_print_text(img, f"Mode: {qr_mode.name} (Hit SPACE to change)", 0, TextPosition.LEFT, FONT_COLOR, 20)
|
||||
cv2_print_text(img, "Press ESC to quit", 1, TextPosition.LEFT, FONT_COLOR, 17)
|
||||
cv2_print_text(img, "Press c,j,k,t,u to save as csv/json/keepass/txt/urls file", 2, TextPosition.LEFT, FONT_COLOR, None)
|
||||
|
||||
cv2_print_text(img, f"{len(otp_urls)} QR code{'s'[:len(otp_urls) != 1]} captured", 0, TextPosition.RIGHT, FONT_COLOR)
|
||||
cv2_print_text(img, f"{len(otps)} otp{'s'[:len(otps) != 1]} extracted", 1, TextPosition.RIGHT, FONT_COLOR)
|
||||
cv2_print_text(img, f"{len(otp_urls)} QR code{'s'[:len(otp_urls) != 1]} captured", 0, TextPosition.RIGHT, FONT_COLOR)
|
||||
cv2_print_text(img, f"{len(otps)} otp{'s'[:len(otps) != 1]} extracted", 1, TextPosition.RIGHT, FONT_COLOR)
|
||||
|
||||
cv2.imshow(WINDOW_NAME, img)
|
||||
cv2.imshow(WINDOW_NAME, img)
|
||||
except cv2.error as e:
|
||||
# Workaround due to Gtk2/3 problem, see #444
|
||||
if e.code == cv2.Error.StsNullPtr:
|
||||
# Window closed
|
||||
break
|
||||
else:
|
||||
raise e
|
||||
|
||||
quit, qr_mode = cv2_handle_pressed_keys(qr_mode, otps)
|
||||
if quit:
|
||||
@@ -404,16 +410,15 @@ def get_color(new_otps_count: int, otp_url: str) -> ColorBGR:
|
||||
return NORMAL_COLOR
|
||||
|
||||
|
||||
# TODO use cv2 types if available
|
||||
def cv2_draw_box(img: Any, raw_pts: Any, color: ColorBGR) -> Any:
|
||||
# TODO use proper cv2 types if available
|
||||
def cv2_draw_box(img: cv2.typing.MatLike, raw_pts: cv2.typing.MatLike | list[tuple[Any, Any]], color: ColorBGR) -> np.ndarray[Any, np.dtype[np.int32]]:
|
||||
pts = np.array([raw_pts], np.int32)
|
||||
pts = pts.reshape((-1, 1, 2))
|
||||
cv2.polylines(img, [pts], True, color, BOX_THICKNESS)
|
||||
return pts
|
||||
|
||||
|
||||
# TODO use cv2 types if available
|
||||
def cv2_print_text(img: Any, text: str, line_number: int, position: TextPosition, color: ColorBGR, opposite_len: Optional[int] = None) -> None:
|
||||
def cv2_print_text(img: cv2.typing.MatLike, text: str, line_number: int, position: TextPosition, color: ColorBGR, opposite_len: Optional[int] = None) -> None:
|
||||
window_dim = cv2.getWindowImageRect(WINDOW_NAME)
|
||||
out_text = text
|
||||
if opposite_len:
|
||||
@@ -445,7 +450,7 @@ def cv2_handle_pressed_keys(qr_mode: QRMode, otps: Otps) -> Tuple[bool, QRMode]:
|
||||
defaultextension='.csv',
|
||||
filetypes=[('CSV', '*.csv'), ('All', '*.*')]
|
||||
)
|
||||
tk_root.update()
|
||||
tk_root.update() # noqa: F821 # workaround flake8 false positive, tk_root is defined globally
|
||||
if len(file_name) > 0:
|
||||
write_csv(file_name, otps)
|
||||
elif (key == ord('j') or key == ord('J')) and is_not_headless():
|
||||
@@ -457,7 +462,7 @@ def cv2_handle_pressed_keys(qr_mode: QRMode, otps: Otps) -> Tuple[bool, QRMode]:
|
||||
defaultextension='.json',
|
||||
filetypes=[('JSON', '*.json'), ('All', '*.*')]
|
||||
)
|
||||
tk_root.update()
|
||||
tk_root.update() # noqa: F821 # workaround flake8 false positive, tk_root is defined globally
|
||||
if len(file_name) > 0:
|
||||
write_json(file_name, otps)
|
||||
elif (key == ord('k') or key == ord('K')) and is_not_headless():
|
||||
@@ -469,7 +474,7 @@ def cv2_handle_pressed_keys(qr_mode: QRMode, otps: Otps) -> Tuple[bool, QRMode]:
|
||||
defaultextension='.csv',
|
||||
filetypes=[('CSV', '*.csv'), ('All', '*.*')]
|
||||
)
|
||||
tk_root.update()
|
||||
tk_root.update() # noqa: F821 # workaround flake8 false positive, tk_root is defined globally
|
||||
if len(file_name) > 0:
|
||||
write_keepass_csv(file_name, otps)
|
||||
elif (key == ord('t') or key == ord('T')) and is_not_headless():
|
||||
@@ -481,15 +486,25 @@ def cv2_handle_pressed_keys(qr_mode: QRMode, otps: Otps) -> Tuple[bool, QRMode]:
|
||||
defaultextension='.txt',
|
||||
filetypes=[('Text', '*.txt'), ('All', '*.*')]
|
||||
)
|
||||
tk_root.update()
|
||||
tk_root.update() # noqa: F821 # workaround flake8 false positive, tk_root is defined globally
|
||||
if len(file_name) > 0:
|
||||
write_txt(file_name, otps, True)
|
||||
elif (key == ord('u') or key == ord('U')) and is_not_headless():
|
||||
if has_no_otps_show_warning(otps):
|
||||
pass
|
||||
else:
|
||||
file_name = tkinter.filedialog.asksaveasfilename(
|
||||
title="Save extracted otp secrets as list of urls",
|
||||
defaultextension='.txt',
|
||||
filetypes=[('Text', '*.txt'), ('All', '*.*')]
|
||||
)
|
||||
tk_root.update() # noqa: F821 # workaround flake8 false positive, tk_root is defined globally
|
||||
if len(file_name) > 0:
|
||||
write_urls(file_name, otps)
|
||||
elif key == 32:
|
||||
qr_mode = next_valid_qr_mode(qr_mode, zbar_available)
|
||||
if verbose >= LogLevel.MORE_VERBOSE: print(f"QR reading mode: {qr_mode}")
|
||||
if cv2.getWindowProperty(WINDOW_NAME, cv2.WND_PROP_VISIBLE) < 1:
|
||||
# Window close clicked
|
||||
quit = True
|
||||
# Do not check for invisible window due to Gtk2/3 problem, see #444
|
||||
return quit, qr_mode
|
||||
|
||||
|
||||
@@ -512,14 +527,20 @@ def extract_otps_from_files(args: Args) -> Otps:
|
||||
|
||||
files_count = urls_count = otps_count = 0
|
||||
if verbose: print(f"Input files: {args.infile}")
|
||||
for infile in args.infile:
|
||||
if verbose >= LogLevel.MORE_VERBOSE: log_verbose(f"Processing infile {infile}")
|
||||
files_count += 1
|
||||
for line in get_otp_urls_from_file(infile, args):
|
||||
if verbose >= LogLevel.MORE_VERBOSE: log_verbose(line)
|
||||
if line.startswith('#') or line == '': continue
|
||||
urls_count += 1
|
||||
otps_count += extract_otp_from_otp_url(line, otps, urls_count, infile, args)
|
||||
for infile_raw in args.infile:
|
||||
expanded_infiles = glob.glob(infile_raw)
|
||||
if not expanded_infiles:
|
||||
expanded_infiles = [infile_raw]
|
||||
if verbose >= LogLevel.DEBUG: log_debug(f"Could not expand input files, fallback to infile {infile_raw}")
|
||||
if verbose >= LogLevel.DEBUG: log_debug(f"Expanded input files: {expanded_infiles}")
|
||||
for infile in expanded_infiles:
|
||||
if verbose >= LogLevel.MORE_VERBOSE: log_verbose(f"Processing infile {infile}")
|
||||
files_count += 1
|
||||
for line in get_otp_urls_from_file(infile, args):
|
||||
if verbose >= LogLevel.MORE_VERBOSE: log_verbose(line)
|
||||
if line.startswith('#') or line == '': continue
|
||||
urls_count += 1
|
||||
otps_count += extract_otp_from_otp_url(line, otps, urls_count, infile, args)
|
||||
if verbose: print(f"Extracted {otps_count} otp{'s'[:otps_count != 1]} from {urls_count} otp url{'s'[:urls_count != 1]} by reading {files_count} infile{'s'[:files_count != 1]}")
|
||||
return otps
|
||||
|
||||
@@ -541,11 +562,7 @@ def get_otp_urls_from_file(filename: str, args: Args) -> OtpUrls:
|
||||
|
||||
def read_lines_from_text_file(filename: str) -> list[str]:
|
||||
if verbose >= LogLevel.DEBUG: print(f"Reading lines of {filename}")
|
||||
# workaround for PYTHON <= 3.9 support encoding
|
||||
if sys.version_info >= (3, 10):
|
||||
finput = fileinput.input(filename, encoding='utf-8')
|
||||
else:
|
||||
finput = fileinput.input(filename)
|
||||
finput = fileinput.input(filename, encoding='utf-8')
|
||||
try:
|
||||
lines = []
|
||||
for line in (line.strip() for line in finput):
|
||||
@@ -577,7 +594,7 @@ def convert_img_to_otp_urls(filename: str, args: Args) -> OtpUrls:
|
||||
stdin = sys.stdin.buffer.read()
|
||||
except AttributeError:
|
||||
# Workaround for pytest, since pytest cannot monkeypatch sys.stdin.buffer
|
||||
stdin = sys.stdin.read() # type: ignore # Workaround for pytest fixtures
|
||||
stdin = sys.stdin.read()
|
||||
if not stdin:
|
||||
log_warn("stdin is empty")
|
||||
try:
|
||||
@@ -664,6 +681,10 @@ def print_otp(otp: Otp, out: Optional[TextIO] = None) -> None:
|
||||
print(otp['url'], file=out)
|
||||
|
||||
|
||||
def write_url(otp: Otp, out: Optional[TextIO] = None) -> None:
|
||||
print(otp['url'], file=out)
|
||||
|
||||
|
||||
def save_qr_image(otp: Otp, dir: str, j: int) -> str:
|
||||
if not (os.path.exists(dir)): os.makedirs(dir, exist_ok=True)
|
||||
pattern = re.compile(r'[\W_]+')
|
||||
@@ -678,7 +699,7 @@ def save_qr_image_file(otp_url: OtpUrl, name: str) -> None:
|
||||
qr.add_data(otp_url)
|
||||
img = qr.make_image(fill_color='black', back_color='white')
|
||||
if verbose: print(f"Saving to {name}")
|
||||
img.save(name)
|
||||
img.save(name) # type: ignore
|
||||
|
||||
|
||||
def print_qr(otp_url: str, out: Optional[TextIO] = None) -> None:
|
||||
@@ -697,6 +718,14 @@ def write_txt(file: str, otps: Otps, write_qr: bool = False) -> None:
|
||||
print(file=outfile)
|
||||
|
||||
|
||||
def write_urls(file: str, otps: Otps) -> None:
|
||||
if file and len(file) > 0 and len(otps) > 0:
|
||||
with open_file_or_stdout(file) as outfile:
|
||||
for otp in otps:
|
||||
write_url(otp, outfile)
|
||||
if not quiet: print(f"Exported {len(otps)} otp{'s'[:len(otps) != 1]} to otpauth url list file {file}")
|
||||
|
||||
|
||||
def write_csv(file: str, otps: Otps) -> None:
|
||||
if file and len(file) > 0 and len(otps) > 0:
|
||||
with open_file_or_stdout_for_csv(file) as outfile:
|
||||
@@ -803,7 +832,7 @@ def check_file_exists(filename: str) -> None:
|
||||
def has_no_otps_show_warning(otps: Otps) -> bool:
|
||||
if len(otps) == 0:
|
||||
tkinter.messagebox.showinfo(title="No data", message="There are no otp secrets to write")
|
||||
tk_root.update() # dispose dialog
|
||||
tk_root.update() # dispose dialog # noqa: F821 # workaround flake8 false positive, tk_root is defined globally
|
||||
return len(otps) == 0
|
||||
|
||||
|
||||
@@ -851,7 +880,7 @@ class PrintVersionAction(argparse.Action):
|
||||
def __init__(self, option_strings: Sequence[str], dest: str, nargs: int = 0, **kwargs: Any) -> None:
|
||||
super().__init__(option_strings, dest, nargs, **kwargs)
|
||||
|
||||
def __call__(self, parser: argparse.ArgumentParser, namespace: Args, values: Union[str, Sequence[Any], None], option_string: Optional[str] = None) -> None:
|
||||
def __call__(self, parser: argparse.ArgumentParser, namespace: Args, values: str | Sequence[Any] | None, option_string: str | None = None) -> None:
|
||||
print_version()
|
||||
parser.exit()
|
||||
|
||||
@@ -886,32 +915,33 @@ def get_raw_version() -> str:
|
||||
return ''
|
||||
|
||||
|
||||
# workaround for PYTHON <= 3.9 use: BaseException | None
|
||||
def log_debug(*values: object, sep: Optional[str] = ' ') -> None:
|
||||
def log_debug(*values: object, sep: str | None = ' ') -> None:
|
||||
if os.name == 'nt':
|
||||
# Workaround "Windows fatal exception: access violation"
|
||||
print(f"\nDEBUG: {str(values[0])}")
|
||||
return
|
||||
|
||||
if colored:
|
||||
print(f"{colorama.Fore.CYAN}\nDEBUG: {str(values[0])}", *values[1:], colorama.Fore.RESET, sep)
|
||||
print(f"{colorama.Fore.CYAN}\nDEBUG: {str(values[0])}", *values[1:], colorama.Fore.RESET, sep=sep)
|
||||
else:
|
||||
print(f"\nDEBUG: {str(values[0])}", *values[1:], sep)
|
||||
print(f"\nDEBUG: {str(values[0])}", *values[1:], sep=sep)
|
||||
|
||||
|
||||
# workaround for PYTHON <= 3.9 use: BaseException | None
|
||||
def log_verbose(msg: str) -> None:
|
||||
print(color(msg, colorama.Fore.CYAN))
|
||||
|
||||
|
||||
# workaround for PYTHON <= 3.9 use: BaseException | None
|
||||
def log_warn(msg: str, exception: Optional[BaseException] = None) -> None:
|
||||
def log_warn(msg: str, exception: BaseException | None = None) -> None:
|
||||
exception_text = "\nException: "
|
||||
eprint(color(f"\nWARN: {msg}{(exception_text + str(exception)) if exception else ''}", colorama.Fore.RED))
|
||||
|
||||
|
||||
# workaround for PYTHON <= 3.9 use: BaseException | None
|
||||
def log_error(msg: str, exception: Optional[BaseException] = None) -> None:
|
||||
def log_error(msg: str, exception: BaseException | None = None) -> None:
|
||||
exception_text = "\nException: "
|
||||
eprint(color(f"\nERROR: {msg}{(exception_text + str(exception)) if exception else ''}", colorama.Fore.RED))
|
||||
|
||||
|
||||
def color(msg: str, color: Optional[str] = None) -> str:
|
||||
def color(msg: str, color: str | None = None) -> str:
|
||||
return f"{color if colored and color else ''}{msg}{colorama.Fore.RESET if colored and color else ''}"
|
||||
|
||||
|
||||
@@ -920,8 +950,7 @@ def eprint(*values: object, **kwargs: Any) -> None:
|
||||
print(*values, file=sys.stderr, **kwargs)
|
||||
|
||||
|
||||
# workaround for PYTHON <= 3.9 use: BaseException | None
|
||||
def abort(msg: str, exception: Optional[BaseException] = None) -> None:
|
||||
def abort(msg: str, exception: BaseException | None = None) -> None:
|
||||
log_error(msg, exception)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# NO CHECKED-IN PROTOBUF GENCODE
|
||||
# source: google_auth.proto
|
||||
# Protobuf Python Version: 7.34.0
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import runtime_version as _runtime_version
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf.internal import builder as _builder
|
||||
_runtime_version.ValidateProtobufRuntimeVersion(
|
||||
_runtime_version.Domain.PUBLIC,
|
||||
7,
|
||||
34,
|
||||
0,
|
||||
'',
|
||||
'google_auth.proto'
|
||||
)
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
@@ -18,9 +29,8 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11google_auth.pr
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google_auth_pb2', _globals)
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
if not _descriptor._USE_C_DESCRIPTORS:
|
||||
DESCRIPTOR._loaded_options = None
|
||||
_globals['_MIGRATIONPAYLOAD']._serialized_start=22
|
||||
_globals['_MIGRATIONPAYLOAD']._serialized_end=461
|
||||
_globals['_MIGRATIONPAYLOAD_OTPPARAMETERS']._serialized_start=176
|
||||
|
||||
@@ -2,34 +2,35 @@
|
||||
@generated by mypy-protobuf. Do not edit manually!
|
||||
isort:skip_file
|
||||
"""
|
||||
import builtins
|
||||
import collections.abc
|
||||
import google.protobuf.descriptor
|
||||
import google.protobuf.internal.containers
|
||||
import google.protobuf.internal.enum_type_wrapper
|
||||
import google.protobuf.message
|
||||
|
||||
from collections import abc as _abc
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf.internal import containers as _containers
|
||||
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
|
||||
import builtins as _builtins
|
||||
import sys
|
||||
import typing
|
||||
import typing as _typing
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
import typing as typing_extensions
|
||||
from typing import TypeAlias as _TypeAlias
|
||||
else:
|
||||
import typing_extensions
|
||||
from typing_extensions import TypeAlias as _TypeAlias
|
||||
|
||||
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
||||
DESCRIPTOR: _descriptor.FileDescriptor
|
||||
|
||||
@typing_extensions.final
|
||||
class MigrationPayload(google.protobuf.message.Message):
|
||||
@_typing.final
|
||||
class MigrationPayload(_message.Message):
|
||||
"""Copied from: https://github.com/beemdevelopment/Aegis/blob/master/app/src/main/proto/google_auth.proto"""
|
||||
|
||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
||||
DESCRIPTOR: _descriptor.Descriptor
|
||||
|
||||
class _Algorithm:
|
||||
ValueType = typing.NewType("ValueType", builtins.int)
|
||||
V: typing_extensions.TypeAlias = ValueType
|
||||
ValueType = _typing.NewType("ValueType", _builtins.int)
|
||||
V: _TypeAlias = ValueType # noqa: Y015
|
||||
|
||||
class _AlgorithmEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[MigrationPayload._Algorithm.ValueType], builtins.type): # noqa: F821
|
||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
||||
class _AlgorithmEnumTypeWrapper(_enum_type_wrapper._EnumTypeWrapper[MigrationPayload._Algorithm.ValueType], _builtins.type):
|
||||
DESCRIPTOR: _descriptor.EnumDescriptor
|
||||
ALGO_INVALID: MigrationPayload._Algorithm.ValueType # 0
|
||||
ALGO_SHA1: MigrationPayload._Algorithm.ValueType # 1
|
||||
|
||||
@@ -38,11 +39,11 @@ class MigrationPayload(google.protobuf.message.Message):
|
||||
ALGO_SHA1: MigrationPayload.Algorithm.ValueType # 1
|
||||
|
||||
class _OtpType:
|
||||
ValueType = typing.NewType("ValueType", builtins.int)
|
||||
V: typing_extensions.TypeAlias = ValueType
|
||||
ValueType = _typing.NewType("ValueType", _builtins.int)
|
||||
V: _TypeAlias = ValueType # noqa: Y015
|
||||
|
||||
class _OtpTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[MigrationPayload._OtpType.ValueType], builtins.type): # noqa: F821
|
||||
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
||||
class _OtpTypeEnumTypeWrapper(_enum_type_wrapper._EnumTypeWrapper[MigrationPayload._OtpType.ValueType], _builtins.type):
|
||||
DESCRIPTOR: _descriptor.EnumDescriptor
|
||||
OTP_INVALID: MigrationPayload._OtpType.ValueType # 0
|
||||
OTP_HOTP: MigrationPayload._OtpType.ValueType # 1
|
||||
OTP_TOTP: MigrationPayload._OtpType.ValueType # 2
|
||||
@@ -52,57 +53,59 @@ class MigrationPayload(google.protobuf.message.Message):
|
||||
OTP_HOTP: MigrationPayload.OtpType.ValueType # 1
|
||||
OTP_TOTP: MigrationPayload.OtpType.ValueType # 2
|
||||
|
||||
@typing_extensions.final
|
||||
class OtpParameters(google.protobuf.message.Message):
|
||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
||||
@_typing.final
|
||||
class OtpParameters(_message.Message):
|
||||
DESCRIPTOR: _descriptor.Descriptor
|
||||
|
||||
SECRET_FIELD_NUMBER: builtins.int
|
||||
NAME_FIELD_NUMBER: builtins.int
|
||||
ISSUER_FIELD_NUMBER: builtins.int
|
||||
ALGORITHM_FIELD_NUMBER: builtins.int
|
||||
DIGITS_FIELD_NUMBER: builtins.int
|
||||
TYPE_FIELD_NUMBER: builtins.int
|
||||
COUNTER_FIELD_NUMBER: builtins.int
|
||||
secret: builtins.bytes
|
||||
name: builtins.str
|
||||
issuer: builtins.str
|
||||
algorithm: global___MigrationPayload.Algorithm.ValueType
|
||||
digits: builtins.int
|
||||
type: global___MigrationPayload.OtpType.ValueType
|
||||
counter: builtins.int
|
||||
SECRET_FIELD_NUMBER: _builtins.int
|
||||
NAME_FIELD_NUMBER: _builtins.int
|
||||
ISSUER_FIELD_NUMBER: _builtins.int
|
||||
ALGORITHM_FIELD_NUMBER: _builtins.int
|
||||
DIGITS_FIELD_NUMBER: _builtins.int
|
||||
TYPE_FIELD_NUMBER: _builtins.int
|
||||
COUNTER_FIELD_NUMBER: _builtins.int
|
||||
secret: _builtins.bytes
|
||||
name: _builtins.str
|
||||
issuer: _builtins.str
|
||||
algorithm: Global___MigrationPayload.Algorithm.ValueType
|
||||
digits: _builtins.int
|
||||
type: Global___MigrationPayload.OtpType.ValueType
|
||||
counter: _builtins.int
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
secret: builtins.bytes = ...,
|
||||
name: builtins.str = ...,
|
||||
issuer: builtins.str = ...,
|
||||
algorithm: global___MigrationPayload.Algorithm.ValueType = ...,
|
||||
digits: builtins.int = ...,
|
||||
type: global___MigrationPayload.OtpType.ValueType = ...,
|
||||
counter: builtins.int = ...,
|
||||
secret: _builtins.bytes = ...,
|
||||
name: _builtins.str = ...,
|
||||
issuer: _builtins.str = ...,
|
||||
algorithm: Global___MigrationPayload.Algorithm.ValueType = ...,
|
||||
digits: _builtins.int = ...,
|
||||
type: Global___MigrationPayload.OtpType.ValueType = ...,
|
||||
counter: _builtins.int = ...,
|
||||
) -> None: ...
|
||||
def ClearField(self, field_name: typing_extensions.Literal["algorithm", b"algorithm", "counter", b"counter", "digits", b"digits", "issuer", b"issuer", "name", b"name", "secret", b"secret", "type", b"type"]) -> None: ...
|
||||
_ClearFieldArgType: _TypeAlias = _typing.Literal["algorithm", b"algorithm", "counter", b"counter", "digits", b"digits", "issuer", b"issuer", "name", b"name", "secret", b"secret", "type", b"type"] # noqa: Y015
|
||||
def ClearField(self, field_name: _ClearFieldArgType) -> None: ...
|
||||
|
||||
OTP_PARAMETERS_FIELD_NUMBER: builtins.int
|
||||
VERSION_FIELD_NUMBER: builtins.int
|
||||
BATCH_SIZE_FIELD_NUMBER: builtins.int
|
||||
BATCH_INDEX_FIELD_NUMBER: builtins.int
|
||||
BATCH_ID_FIELD_NUMBER: builtins.int
|
||||
@property
|
||||
def otp_parameters(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___MigrationPayload.OtpParameters]: ...
|
||||
version: builtins.int
|
||||
batch_size: builtins.int
|
||||
batch_index: builtins.int
|
||||
batch_id: builtins.int
|
||||
OTP_PARAMETERS_FIELD_NUMBER: _builtins.int
|
||||
VERSION_FIELD_NUMBER: _builtins.int
|
||||
BATCH_SIZE_FIELD_NUMBER: _builtins.int
|
||||
BATCH_INDEX_FIELD_NUMBER: _builtins.int
|
||||
BATCH_ID_FIELD_NUMBER: _builtins.int
|
||||
version: _builtins.int
|
||||
batch_size: _builtins.int
|
||||
batch_index: _builtins.int
|
||||
batch_id: _builtins.int
|
||||
@_builtins.property
|
||||
def otp_parameters(self) -> _containers.RepeatedCompositeFieldContainer[Global___MigrationPayload.OtpParameters]: ...
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
otp_parameters: collections.abc.Iterable[global___MigrationPayload.OtpParameters] | None = ...,
|
||||
version: builtins.int = ...,
|
||||
batch_size: builtins.int = ...,
|
||||
batch_index: builtins.int = ...,
|
||||
batch_id: builtins.int = ...,
|
||||
otp_parameters: _abc.Iterable[Global___MigrationPayload.OtpParameters] | None = ...,
|
||||
version: _builtins.int = ...,
|
||||
batch_size: _builtins.int = ...,
|
||||
batch_index: _builtins.int = ...,
|
||||
batch_id: _builtins.int = ...,
|
||||
) -> None: ...
|
||||
def ClearField(self, field_name: typing_extensions.Literal["batch_id", b"batch_id", "batch_index", b"batch_index", "batch_size", b"batch_size", "otp_parameters", b"otp_parameters", "version", b"version"]) -> None: ...
|
||||
_ClearFieldArgType: _TypeAlias = _typing.Literal["batch_id", b"batch_id", "batch_index", b"batch_index", "batch_size", b"batch_size", "otp_parameters", b"otp_parameters", "version", b"version"] # noqa: Y015
|
||||
def ClearField(self, field_name: _ClearFieldArgType) -> None: ...
|
||||
|
||||
global___MigrationPayload = MigrationPayload
|
||||
Global___MigrationPayload: _TypeAlias = MigrationPayload # noqa: Y015
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
QReader installed: True
|
||||
CV2 version: 4.7.0
|
||||
CV2 version: 4.10.0
|
||||
QR reading mode: ZBAR
|
||||
|
||||
Input files: ['example_export.txt']
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
QReader installed: True
|
||||
CV2 version: 4.7.0
|
||||
CV2 version: 4.10.0
|
||||
QR reading mode: ZBAR
|
||||
|
||||
Input files: ['example_export.txt']
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
QReader installed: True
|
||||
CV2 version: 4.7.0
|
||||
CV2 version: 4.10.0
|
||||
QR reading mode: ZBAR
|
||||
|
||||
Version: extract_otp_secrets 2.0.2.post50+git.158245dd.dirty Linux x86_64 Python 3.11.1 (CPython/called as script)
|
||||
Version: extract_otp_secrets 2.8.4.post4+git.7ce765dd.dirty Linux x86_64 Python 3.11.10 (CPython/called as script)
|
||||
|
||||
Input files: ['example_export.txt']
|
||||
|
||||
DEBUG: Expanded input files: ['example_export.txt']
|
||||
Processing infile example_export.txt
|
||||
Reading lines of example_export.txt
|
||||
# 2FA example from https://www.raspberrypi.org/blog/setting-up-two-factor-authentication-on-your-raspberry-pi/
|
||||
@@ -32,16 +34,17 @@ otpauth-migration://offline?data=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8O
|
||||
# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
|
||||
otpauth-migration://offline?data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B
|
||||
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B', fragment='')
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B', fragment='')
|
||||
|
||||
DEBUG: querystring params={'data': ['CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B']}
|
||||
DEBUG: querystring params={'data': ['CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B']}
|
||||
|
||||
DEBUG: data_base64=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B
|
||||
DEBUG: data_base64=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B
|
||||
|
||||
DEBUG: data_base64_fixed=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B
|
||||
DEBUG: data_base64_fixed=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B
|
||||
|
||||
DEBUG:
|
||||
1. Payload Line otp_parameters {
|
||||
1. Payload Line
|
||||
otp_parameters {
|
||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||
name: "pi@raspberrypi"
|
||||
issuer: "raspberrypi"
|
||||
@@ -52,12 +55,11 @@ DEBUG:
|
||||
version: 1
|
||||
batch_size: 1
|
||||
batch_id: -1320898453
|
||||
|
||||
|
||||
|
||||
1. Secret
|
||||
|
||||
DEBUG: OTP enum type: OTP_TOTP
|
||||
DEBUG: OTP enum type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
@@ -68,16 +70,17 @@ otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspber
|
||||
# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
otpauth-migration://offline?data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4%2F%2F%2F%2F%2FwE%3D
|
||||
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4%2F%2F%2F%2F%2FwE%3D', fragment='')
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4%2F%2F%2F%2F%2FwE%3D', fragment='')
|
||||
|
||||
DEBUG: querystring params={'data': ['CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE=']}
|
||||
DEBUG: querystring params={'data': ['CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE=']}
|
||||
|
||||
DEBUG: data_base64=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE=
|
||||
DEBUG: data_base64=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE=
|
||||
|
||||
DEBUG: data_base64_fixed=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE=
|
||||
DEBUG: data_base64_fixed=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE=
|
||||
|
||||
DEBUG:
|
||||
2. Payload Line otp_parameters {
|
||||
2. Payload Line
|
||||
otp_parameters {
|
||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||
name: "pi@raspberrypi"
|
||||
algorithm: ALGO_SHA1
|
||||
@@ -87,12 +90,11 @@ DEBUG:
|
||||
version: 1
|
||||
batch_size: 1
|
||||
batch_id: -2094403140
|
||||
|
||||
|
||||
|
||||
2. Secret
|
||||
|
||||
DEBUG: OTP enum type: OTP_TOTP
|
||||
DEBUG: OTP enum type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: totp
|
||||
@@ -103,16 +105,17 @@ otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
otpauth-migration://offline?data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa%2Bf%2F%2F%2F%2F8B
|
||||
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa%2Bf%2F%2F%2F%2F8B', fragment='')
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa%2Bf%2F%2F%2F%2F8B', fragment='')
|
||||
|
||||
DEBUG: querystring params={'data': ['CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B']}
|
||||
DEBUG: querystring params={'data': ['CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B']}
|
||||
|
||||
DEBUG: data_base64=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B
|
||||
DEBUG: data_base64=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B
|
||||
|
||||
DEBUG: data_base64_fixed=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B
|
||||
DEBUG: data_base64_fixed=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B
|
||||
|
||||
DEBUG:
|
||||
3. Payload Line otp_parameters {
|
||||
3. Payload Line
|
||||
otp_parameters {
|
||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||
name: "pi@raspberrypi"
|
||||
algorithm: ALGO_SHA1
|
||||
@@ -130,12 +133,11 @@ otp_parameters {
|
||||
version: 1
|
||||
batch_size: 1
|
||||
batch_id: -1822886384
|
||||
|
||||
|
||||
|
||||
3. Secret
|
||||
|
||||
DEBUG: OTP enum type: OTP_TOTP
|
||||
DEBUG: OTP enum type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: totp
|
||||
@@ -144,7 +146,7 @@ otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
|
||||
4. Secret
|
||||
|
||||
DEBUG: OTP enum type: OTP_TOTP
|
||||
DEBUG: OTP enum type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
@@ -155,16 +157,17 @@ otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspber
|
||||
# otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4
|
||||
otpauth-migration://offline?data=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6%2F%2F%2F%2F%2FwE%3D
|
||||
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6%2F%2F%2F%2F%2FwE%3D', fragment='')
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6%2F%2F%2F%2F%2FwE%3D', fragment='')
|
||||
|
||||
DEBUG: querystring params={'data': ['CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE=']}
|
||||
DEBUG: querystring params={'data': ['CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE=']}
|
||||
|
||||
DEBUG: data_base64=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE=
|
||||
DEBUG: data_base64=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE=
|
||||
|
||||
DEBUG: data_base64_fixed=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE=
|
||||
DEBUG: data_base64_fixed=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE=
|
||||
|
||||
DEBUG:
|
||||
4. Payload Line otp_parameters {
|
||||
4. Payload Line
|
||||
otp_parameters {
|
||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||
name: "hotp demo"
|
||||
algorithm: ALGO_SHA1
|
||||
@@ -175,12 +178,11 @@ DEBUG:
|
||||
version: 1
|
||||
batch_size: 1
|
||||
batch_id: -1558849573
|
||||
|
||||
|
||||
|
||||
5. Secret
|
||||
|
||||
DEBUG: OTP enum type: OTP_HOTP
|
||||
DEBUG: OTP enum type: OTP_HOTP
|
||||
Name: hotp demo
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: hotp
|
||||
@@ -192,16 +194,17 @@ otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4
|
||||
# Name: "encoding: ¿äÄéÉ? (demo)"
|
||||
otpauth-migration://offline?data=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv%2F%2F%2F%2F%2F%2FAQ%3D%3D
|
||||
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv%2F%2F%2F%2F%2F%2FAQ%3D%3D', fragment='')
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv%2F%2F%2F%2F%2F%2FAQ%3D%3D', fragment='')
|
||||
|
||||
DEBUG: querystring params={'data': ['CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ==']}
|
||||
DEBUG: querystring params={'data': ['CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ==']}
|
||||
|
||||
DEBUG: data_base64=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ==
|
||||
DEBUG: data_base64=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ==
|
||||
|
||||
DEBUG: data_base64_fixed=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ==
|
||||
DEBUG: data_base64_fixed=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ==
|
||||
|
||||
DEBUG:
|
||||
5. Payload Line otp_parameters {
|
||||
5. Payload Line
|
||||
otp_parameters {
|
||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||
name: "encoding: ¿äÄéÉ? (demo)"
|
||||
algorithm: ALGO_SHA1
|
||||
@@ -211,12 +214,11 @@ DEBUG:
|
||||
version: 1
|
||||
batch_size: 1
|
||||
batch_id: -171198419
|
||||
|
||||
|
||||
|
||||
6. Secret
|
||||
|
||||
DEBUG: OTP enum type: OTP_TOTP
|
||||
DEBUG: OTP enum type: OTP_TOTP
|
||||
Name: encoding: ¿äÄéÉ? (demo)
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: totp
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
QReader installed: True
|
||||
CV2 version: 4.7.0
|
||||
CV2 version: 4.10.0
|
||||
QR reading mode: ZBAR
|
||||
|
||||
Input files: ['example_export.txt']
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
QReader installed: True
|
||||
CV2 version: 4.7.0
|
||||
CV2 version: 4.10.0
|
||||
QR reading mode: ZBAR
|
||||
|
||||
Input files: ['example_export.txt']
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
QReader installed: True
|
||||
CV2 version: 4.7.0
|
||||
CV2 version: 4.10.0
|
||||
QR reading mode: ZBAR
|
||||
|
||||
Version: extract_otp_secrets 2.0.2.post50+git.158245dd.dirty Linux x86_64 Python 3.11.1 (CPython/called as script)
|
||||
Version: extract_otp_secrets 2.8.4.post4+git.7ce765dd.dirty Linux x86_64 Python 3.11.10 (CPython/called as script)
|
||||
|
||||
Input files: ['example_export.txt']
|
||||
[36m
|
||||
DEBUG: Expanded input files: ['example_export.txt'] [39m
|
||||
[36mProcessing infile example_export.txt[39m
|
||||
Reading lines of example_export.txt
|
||||
[36m# 2FA example from https://www.raspberrypi.org/blog/setting-up-two-factor-authentication-on-your-raspberry-pi/[39m
|
||||
@@ -32,16 +34,17 @@ Reading lines of example_export.txt
|
||||
[36m# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi[39m
|
||||
[36motpauth-migration://offline?data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B[39m
|
||||
[36m
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B', fragment='') [39m
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B', fragment='') [39m
|
||||
[36m
|
||||
DEBUG: querystring params={'data': ['CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B']} [39m
|
||||
DEBUG: querystring params={'data': ['CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B']} [39m
|
||||
[36m
|
||||
DEBUG: data_base64=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B [39m
|
||||
DEBUG: data_base64=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B [39m
|
||||
[36m
|
||||
DEBUG: data_base64_fixed=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B [39m
|
||||
DEBUG: data_base64_fixed=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK+/////8B [39m
|
||||
[36m
|
||||
DEBUG:
|
||||
1. Payload Line otp_parameters {
|
||||
1. Payload Line
|
||||
otp_parameters {
|
||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||
name: "pi@raspberrypi"
|
||||
issuer: "raspberrypi"
|
||||
@@ -52,12 +55,12 @@ DEBUG:
|
||||
version: 1
|
||||
batch_size: 1
|
||||
batch_id: -1320898453
|
||||
[39m
|
||||
|
||||
[39m
|
||||
|
||||
1. Secret
|
||||
[36m
|
||||
DEBUG: OTP enum type: OTP_TOTP [39m
|
||||
DEBUG: OTP enum type: OTP_TOTP [39m
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
@@ -68,16 +71,17 @@ otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspber
|
||||
[36m# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY[39m
|
||||
[36motpauth-migration://offline?data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4%2F%2F%2F%2F%2FwE%3D[39m
|
||||
[36m
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4%2F%2F%2F%2F%2FwE%3D', fragment='') [39m
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4%2F%2F%2F%2F%2FwE%3D', fragment='') [39m
|
||||
[36m
|
||||
DEBUG: querystring params={'data': ['CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE=']} [39m
|
||||
DEBUG: querystring params={'data': ['CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE=']} [39m
|
||||
[36m
|
||||
DEBUG: data_base64=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE= [39m
|
||||
DEBUG: data_base64=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE= [39m
|
||||
[36m
|
||||
DEBUG: data_base64_fixed=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE= [39m
|
||||
DEBUG: data_base64_fixed=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4/////wE= [39m
|
||||
[36m
|
||||
DEBUG:
|
||||
2. Payload Line otp_parameters {
|
||||
2. Payload Line
|
||||
otp_parameters {
|
||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||
name: "pi@raspberrypi"
|
||||
algorithm: ALGO_SHA1
|
||||
@@ -87,12 +91,12 @@ DEBUG:
|
||||
version: 1
|
||||
batch_size: 1
|
||||
batch_id: -2094403140
|
||||
[39m
|
||||
|
||||
[39m
|
||||
|
||||
2. Secret
|
||||
[36m
|
||||
DEBUG: OTP enum type: OTP_TOTP [39m
|
||||
DEBUG: OTP enum type: OTP_TOTP [39m
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: totp
|
||||
@@ -103,16 +107,17 @@ otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
[36m# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY[39m
|
||||
[36motpauth-migration://offline?data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa%2Bf%2F%2F%2F%2F8B[39m
|
||||
[36m
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa%2Bf%2F%2F%2F%2F8B', fragment='') [39m
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa%2Bf%2F%2F%2F%2F8B', fragment='') [39m
|
||||
[36m
|
||||
DEBUG: querystring params={'data': ['CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B']} [39m
|
||||
DEBUG: querystring params={'data': ['CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B']} [39m
|
||||
[36m
|
||||
DEBUG: data_base64=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B [39m
|
||||
DEBUG: data_base64=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B [39m
|
||||
[36m
|
||||
DEBUG: data_base64_fixed=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B [39m
|
||||
DEBUG: data_base64_fixed=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa+f////8B [39m
|
||||
[36m
|
||||
DEBUG:
|
||||
3. Payload Line otp_parameters {
|
||||
3. Payload Line
|
||||
otp_parameters {
|
||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||
name: "pi@raspberrypi"
|
||||
algorithm: ALGO_SHA1
|
||||
@@ -130,12 +135,12 @@ otp_parameters {
|
||||
version: 1
|
||||
batch_size: 1
|
||||
batch_id: -1822886384
|
||||
[39m
|
||||
|
||||
[39m
|
||||
|
||||
3. Secret
|
||||
[36m
|
||||
DEBUG: OTP enum type: OTP_TOTP [39m
|
||||
DEBUG: OTP enum type: OTP_TOTP [39m
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: totp
|
||||
@@ -144,7 +149,7 @@ otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
|
||||
4. Secret
|
||||
[36m
|
||||
DEBUG: OTP enum type: OTP_TOTP [39m
|
||||
DEBUG: OTP enum type: OTP_TOTP [39m
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
@@ -155,16 +160,17 @@ otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspber
|
||||
[36m# otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4[39m
|
||||
[36motpauth-migration://offline?data=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6%2F%2F%2F%2F%2FwE%3D[39m
|
||||
[36m
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6%2F%2F%2F%2F%2FwE%3D', fragment='') [39m
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6%2F%2F%2F%2F%2FwE%3D', fragment='') [39m
|
||||
[36m
|
||||
DEBUG: querystring params={'data': ['CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE=']} [39m
|
||||
DEBUG: querystring params={'data': ['CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE=']} [39m
|
||||
[36m
|
||||
DEBUG: data_base64=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE= [39m
|
||||
DEBUG: data_base64=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE= [39m
|
||||
[36m
|
||||
DEBUG: data_base64_fixed=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE= [39m
|
||||
DEBUG: data_base64_fixed=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6/////wE= [39m
|
||||
[36m
|
||||
DEBUG:
|
||||
4. Payload Line otp_parameters {
|
||||
4. Payload Line
|
||||
otp_parameters {
|
||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||
name: "hotp demo"
|
||||
algorithm: ALGO_SHA1
|
||||
@@ -175,12 +181,12 @@ DEBUG:
|
||||
version: 1
|
||||
batch_size: 1
|
||||
batch_id: -1558849573
|
||||
[39m
|
||||
|
||||
[39m
|
||||
|
||||
5. Secret
|
||||
[36m
|
||||
DEBUG: OTP enum type: OTP_HOTP [39m
|
||||
DEBUG: OTP enum type: OTP_HOTP [39m
|
||||
Name: hotp demo
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: hotp
|
||||
@@ -192,16 +198,17 @@ otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4
|
||||
[36m# Name: "encoding: ¿äÄéÉ? (demo)"[39m
|
||||
[36motpauth-migration://offline?data=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv%2F%2F%2F%2F%2F%2FAQ%3D%3D[39m
|
||||
[36m
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv%2F%2F%2F%2F%2F%2FAQ%3D%3D', fragment='') [39m
|
||||
DEBUG: parsed_url=ParseResult(scheme='otpauth-migration', netloc='offline', path='', params='', query='data=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv%2F%2F%2F%2F%2F%2FAQ%3D%3D', fragment='') [39m
|
||||
[36m
|
||||
DEBUG: querystring params={'data': ['CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ==']} [39m
|
||||
DEBUG: querystring params={'data': ['CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ==']} [39m
|
||||
[36m
|
||||
DEBUG: data_base64=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ== [39m
|
||||
DEBUG: data_base64=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ== [39m
|
||||
[36m
|
||||
DEBUG: data_base64_fixed=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ== [39m
|
||||
DEBUG: data_base64_fixed=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv//////AQ== [39m
|
||||
[36m
|
||||
DEBUG:
|
||||
5. Payload Line otp_parameters {
|
||||
5. Payload Line
|
||||
otp_parameters {
|
||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||
name: "encoding: ¿äÄéÉ? (demo)"
|
||||
algorithm: ALGO_SHA1
|
||||
@@ -211,12 +218,12 @@ DEBUG:
|
||||
version: 1
|
||||
batch_size: 1
|
||||
batch_id: -171198419
|
||||
[39m
|
||||
|
||||
[39m
|
||||
|
||||
6. Secret
|
||||
[36m
|
||||
DEBUG: OTP enum type: OTP_TOTP [39m
|
||||
DEBUG: OTP enum type: OTP_TOTP [39m
|
||||
Name: encoding: ¿äÄéÉ? (demo)
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: totp
|
||||
|
||||
6
tests/data/url_list_output.txt
Normal file
6
tests/data/url_list_output.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
|
||||
otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
|
||||
otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4
|
||||
otpauth://totp/encoding%3A%20%C2%BF%C3%A4%C3%84%C3%A9%C3%89%3F%20%28demo%29?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
@@ -418,6 +418,37 @@ def test_extract_txt_stdout_only_comments(capsys: pytest.CaptureFixture[str]) ->
|
||||
assert captured.err == ''
|
||||
|
||||
|
||||
def test_extract_urls(capsys: pytest.CaptureFixture[str], tmp_path: pathlib.Path) -> None:
|
||||
# Arrange
|
||||
output_file = str(tmp_path / 'test_example_url_list.txt')
|
||||
|
||||
# Act
|
||||
extract_otp_secrets.main(['-q', '-u', output_file, 'example_export.txt'])
|
||||
|
||||
# Assert
|
||||
expected_txt = read_file_to_str('tests/data/url_list_output.txt')
|
||||
actual_txt = read_file_to_str(output_file)
|
||||
|
||||
assert actual_txt == expected_txt
|
||||
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert captured.out == ''
|
||||
assert captured.err == ''
|
||||
|
||||
|
||||
def test_extract_urls_stdout(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
# Act
|
||||
extract_otp_secrets.main(['-u', '-', 'example_export.txt'])
|
||||
|
||||
# Assert
|
||||
expected_txt = read_file_to_str('tests/data/url_list_output.txt')
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert captured.out == expected_txt
|
||||
assert captured.err == ''
|
||||
|
||||
|
||||
def test_extract_not_encoded_plus(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
# Act
|
||||
extract_otp_secrets.main(['tests/data/test_plus_problem_export.txt'])
|
||||
@@ -528,13 +559,16 @@ def test_normalize_bytes() -> None:
|
||||
# Generate verbose output:
|
||||
# for color in '' '-n'; do for level in '' '-v' '-vv' '-vvv'; do python3.11 src/extract_otp_secrets.py example_export.txt $color $level > tests/data/print_verbose_output$color$level.txt; done; done
|
||||
# workaround for PYTHON <= 3.10
|
||||
@pytest.mark.skipif(sys.version_info < (3, 10), reason="fileinput.input encoding exists since PYTHON 3.10")
|
||||
@pytest.mark.parametrize("verbose_level", ['', '-v', '-vv', '-vvv'])
|
||||
@pytest.mark.skipif(sys.version_info < (3, 10), reason="fileinput.input encoding exists since PYTHON 3.10 OR Windows fatal exception: access violation")
|
||||
@pytest.mark.parametrize("verbose_level", ['', '-v', '-vv', '-vvv' if not sys.platform.startswith("win") else None])
|
||||
@pytest.mark.parametrize("color", ['', '-n'])
|
||||
def test_extract_verbose(verbose_level: str, color: str, capsys: pytest.CaptureFixture[str], relaxed: bool) -> None:
|
||||
args = ['example_export.txt']
|
||||
if verbose_level:
|
||||
args.append(verbose_level)
|
||||
elif verbose_level is None:
|
||||
print('Skip parameterized test\n')
|
||||
return
|
||||
if color:
|
||||
args.append(color)
|
||||
# Act
|
||||
@@ -818,7 +852,14 @@ data=XXXX
|
||||
Exception: unpack requires a buffer of 4 bytes
|
||||
'''
|
||||
|
||||
assert captured.err == first_expected_stderr or captured.err == second_expected_stderr
|
||||
#
|
||||
third_expected_stderr = '''
|
||||
ERROR: Cannot decode otpauth-migration migration payload.
|
||||
data=XXXX
|
||||
Exception: Error parsing message with type 'MigrationPayload'
|
||||
'''
|
||||
|
||||
assert captured.err == first_expected_stderr or captured.err == second_expected_stderr or captured.err == third_expected_stderr
|
||||
assert captured.out == ''
|
||||
assert e.value.code == 1
|
||||
assert e.type == SystemExit
|
||||
@@ -831,19 +872,8 @@ def test_wrong_content(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
# Assert
|
||||
captured = capsys.readouterr()
|
||||
|
||||
expected_stderr = '''
|
||||
WARN: input is not a otpauth-migration:// url
|
||||
source: tests/data/test_export_wrong_content.txt
|
||||
input: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||
Maybe a wrong file was given
|
||||
|
||||
ERROR: could not parse query parameter in input url
|
||||
source: tests/data/test_export_wrong_content.txt
|
||||
url: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||
'''
|
||||
|
||||
assert captured.out == ''
|
||||
assert captured.err == expected_stderr
|
||||
assert captured.err == EXPECTED_STDERR_OTP_URL_WRONG
|
||||
|
||||
|
||||
def test_one_wrong_file(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
@@ -853,19 +883,8 @@ def test_one_wrong_file(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
# Assert
|
||||
captured = capsys.readouterr()
|
||||
|
||||
expected_stderr = '''
|
||||
WARN: input is not a otpauth-migration:// url
|
||||
source: tests/data/test_export_wrong_content.txt
|
||||
input: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||
Maybe a wrong file was given
|
||||
|
||||
ERROR: could not parse query parameter in input url
|
||||
source: tests/data/test_export_wrong_content.txt
|
||||
url: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||
'''
|
||||
|
||||
assert captured.out == EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT
|
||||
assert captured.err == expected_stderr
|
||||
assert captured.err == EXPECTED_STDERR_OTP_URL_WRONG
|
||||
|
||||
|
||||
def test_one_wrong_file_colored(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
@@ -875,19 +894,8 @@ def test_one_wrong_file_colored(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
# Assert
|
||||
captured = capsys.readouterr()
|
||||
|
||||
expected_stderr = f'''{colorama.Fore.RED}
|
||||
WARN: input is not a otpauth-migration:// url
|
||||
source: tests/data/test_export_wrong_content.txt
|
||||
input: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||
Maybe a wrong file was given{colorama.Fore.RESET}
|
||||
{colorama.Fore.RED}
|
||||
ERROR: could not parse query parameter in input url
|
||||
source: tests/data/test_export_wrong_content.txt
|
||||
url: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.{colorama.Fore.RESET}
|
||||
'''
|
||||
|
||||
assert captured.out == EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT
|
||||
assert captured.err == expected_stderr
|
||||
assert captured.err == EXPECTED_STDERR_COLORED_OTP_URL_WRONG
|
||||
|
||||
|
||||
def test_one_wrong_line(capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
@@ -959,6 +967,46 @@ def test_img_qr_reader_from_file_happy_path(capsys: pytest.CaptureFixture[str])
|
||||
assert captured.err == ''
|
||||
|
||||
|
||||
@pytest.mark.qreader
|
||||
def test_img_qr_reader_but_no_otp_from_file(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
# Act
|
||||
extract_otp_secrets.main(['-n', 'tests/data/qr_but_without_otp.png'])
|
||||
|
||||
# Assert
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert captured.out == ''
|
||||
assert captured.err == EXPECTED_STDERR_NO_OTP_URL
|
||||
|
||||
|
||||
@pytest.mark.qreader
|
||||
def test_img_qr_reader_from_wildcard(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
# Act
|
||||
extract_otp_secrets.main(['-n', 'tests/data/*.png'])
|
||||
|
||||
# Assert
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert captured.out == EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT_PNG
|
||||
assert normalize_testfile_path(captured.err) == EXPECTED_STDERR_NO_OTP_URL
|
||||
|
||||
|
||||
def normalize_testfile_path(text: str) -> str:
|
||||
return text.replace('tests/data\\', 'tests/data/') if sys.platform.startswith("win") else text
|
||||
|
||||
|
||||
@pytest.mark.qreader
|
||||
def test_img_qr_reader_from_multiple_files(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
# Act
|
||||
extract_otp_secrets.main(['-n', 'tests/data/test_googleauth_export.png', 'tests/data/text_masquerading_as_image.jpeg'])
|
||||
|
||||
# Assert
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert captured.out == EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT_PNG
|
||||
assert captured.err == EXPECTED_STDERR_BAD_IMAGE
|
||||
|
||||
|
||||
@pytest.mark.qreader
|
||||
def test_img_qr_reader_by_parameter(capsys: pytest.CaptureFixture[str], qr_mode: str) -> None:
|
||||
# Act
|
||||
@@ -1003,24 +1051,7 @@ def test_img_qr_reader_from_stdin(capsys: pytest.CaptureFixture[str], monkeypatc
|
||||
# Assert
|
||||
captured = capsys.readouterr()
|
||||
|
||||
expected_stdout = '''Name: Test1:test1@example1.com
|
||||
Secret: JBSWY3DPEHPK3PXP
|
||||
Issuer: Test1
|
||||
Type: totp
|
||||
|
||||
Name: Test2:test2@example2.com
|
||||
Secret: JBSWY3DPEHPK3PXQ
|
||||
Issuer: Test2
|
||||
Type: totp
|
||||
|
||||
Name: Test3:test3@example3.com
|
||||
Secret: JBSWY3DPEHPK3PXR
|
||||
Issuer: Test3
|
||||
Type: totp
|
||||
|
||||
'''
|
||||
|
||||
assert captured.out == expected_stdout
|
||||
assert captured.out == EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT_PNG
|
||||
assert captured.err == ''
|
||||
|
||||
|
||||
@@ -1105,19 +1136,9 @@ def test_non_image_file(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
|
||||
# Assert
|
||||
captured = capsys.readouterr()
|
||||
expected_stderr = '''
|
||||
WARN: input is not a otpauth-migration:// url
|
||||
source: tests/data/text_masquerading_as_image.jpeg
|
||||
input: This is just a text file masquerading as an image file.
|
||||
Maybe a wrong file was given
|
||||
|
||||
ERROR: could not parse query parameter in input url
|
||||
source: tests/data/text_masquerading_as_image.jpeg
|
||||
url: This is just a text file masquerading as an image file.
|
||||
'''
|
||||
|
||||
assert captured.err == expected_stderr
|
||||
assert captured.out == ''
|
||||
assert captured.err == EXPECTED_STDERR_BAD_IMAGE
|
||||
|
||||
|
||||
def test_next_valid_qr_mode() -> None:
|
||||
@@ -1171,3 +1192,47 @@ Issuer: Test3
|
||||
Type: totp
|
||||
|
||||
'''
|
||||
|
||||
EXPECTED_STDERR_OTP_URL_WRONG = '''
|
||||
WARN: input is not a otpauth-migration:// url
|
||||
source: tests/data/test_export_wrong_content.txt
|
||||
input: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||
Maybe a wrong file was given
|
||||
|
||||
ERROR: could not parse query parameter in input url
|
||||
source: tests/data/test_export_wrong_content.txt
|
||||
url: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||
'''
|
||||
|
||||
EXPECTED_STDERR_COLORED_OTP_URL_WRONG = f'''{colorama.Fore.RED}
|
||||
WARN: input is not a otpauth-migration:// url
|
||||
source: tests/data/test_export_wrong_content.txt
|
||||
input: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||
Maybe a wrong file was given{colorama.Fore.RESET}
|
||||
{colorama.Fore.RED}
|
||||
ERROR: could not parse query parameter in input url
|
||||
source: tests/data/test_export_wrong_content.txt
|
||||
url: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.{colorama.Fore.RESET}
|
||||
'''
|
||||
|
||||
EXPECTED_STDERR_NO_OTP_URL = '''
|
||||
WARN: input is not a otpauth-migration:// url
|
||||
source: tests/data/qr_but_without_otp.png
|
||||
input: NOT A otpauth-migration:// URL
|
||||
Maybe a wrong file was given
|
||||
|
||||
ERROR: could not parse query parameter in input url
|
||||
source: tests/data/qr_but_without_otp.png
|
||||
url: NOT A otpauth-migration:// URL
|
||||
'''
|
||||
|
||||
EXPECTED_STDERR_BAD_IMAGE = '''
|
||||
WARN: input is not a otpauth-migration:// url
|
||||
source: tests/data/text_masquerading_as_image.jpeg
|
||||
input: This is just a text file masquerading as an image file.
|
||||
Maybe a wrong file was given
|
||||
|
||||
ERROR: could not parse query parameter in input url
|
||||
source: tests/data/text_masquerading_as_image.jpeg
|
||||
url: This is just a text file masquerading as an image file.
|
||||
'''
|
||||
|
||||
@@ -23,12 +23,11 @@ import re
|
||||
import shutil
|
||||
import sys
|
||||
import pathlib
|
||||
from typing import BinaryIO, Any, Union, List
|
||||
from typing import BinaryIO, Any, List
|
||||
|
||||
|
||||
# Types
|
||||
# workaround for PYTHON <= 3.9: Workaround for str | pathlib.Path
|
||||
PathLike = Union[str, pathlib.Path]
|
||||
PathLike = str | pathlib.Path
|
||||
|
||||
|
||||
# Ref. https://stackoverflow.com/a/16571630
|
||||
|
||||
376
uv.lock
generated
Normal file
376
uv.lock
generated
Normal file
@@ -0,0 +1,376 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.10, <4"
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.11'",
|
||||
"python_full_version < '3.11'",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "extract-otp-secrets"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "colorama" },
|
||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||
{ name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "opencv-contrib-python" },
|
||||
{ name = "pillow" },
|
||||
{ name = "protobuf" },
|
||||
{ name = "pyzbar" },
|
||||
{ name = "qrcode" },
|
||||
{ name = "qreader" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "colorama", specifier = ">=0.4.6" },
|
||||
{ name = "numpy", marker = "python_full_version == '3.10.*'", specifier = ">=2.2,<2.3" },
|
||||
{ name = "numpy", marker = "python_full_version >= '3.11'", specifier = ">=2.4.1,<3.0" },
|
||||
{ name = "opencv-contrib-python", specifier = ">=4.13.0" },
|
||||
{ name = "pillow" },
|
||||
{ name = "protobuf", specifier = ">=7.34.0" },
|
||||
{ name = "pyzbar" },
|
||||
{ name = "qrcode" },
|
||||
{ name = "qreader", specifier = "<2.0.0" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numpy"
|
||||
version = "2.2.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version < '3.11'",
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numpy"
|
||||
version = "2.4.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.11'",
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/57/fd/0005efbd0af48e55eb3c7208af93f2862d4b1a56cd78e84309a2d959208d/numpy-2.4.2.tar.gz", hash = "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae", size = 20723651, upload-time = "2026-01-31T23:13:10.135Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/44/71852273146957899753e69986246d6a176061ea183407e95418c2aa4d9a/numpy-2.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7e88598032542bd49af7c4747541422884219056c268823ef6e5e89851c8825", size = 16955478, upload-time = "2026-01-31T23:10:25.623Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/41/5d17d4058bd0cd96bcbd4d9ff0fb2e21f52702aab9a72e4a594efa18692f/numpy-2.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7edc794af8b36ca37ef5fcb5e0d128c7e0595c7b96a2318d1badb6fcd8ee86b1", size = 14965467, upload-time = "2026-01-31T23:10:28.186Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/48/fb1ce8136c19452ed15f033f8aee91d5defe515094e330ce368a0647846f/numpy-2.4.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:6e9f61981ace1360e42737e2bae58b27bf28a1b27e781721047d84bd754d32e7", size = 5475172, upload-time = "2026-01-31T23:10:30.848Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/a9/3feb49f17bbd1300dd2570432961f5c8a4ffeff1db6f02c7273bd020a4c9/numpy-2.4.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cb7bbb88aa74908950d979eeaa24dbdf1a865e3c7e45ff0121d8f70387b55f73", size = 6805145, upload-time = "2026-01-31T23:10:32.352Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/39/fdf35cbd6d6e2fcad42fcf85ac04a85a0d0fbfbf34b30721c98d602fd70a/numpy-2.4.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f069069931240b3fc703f1e23df63443dbd6390614c8c44a87d96cd0ec81eb1", size = 15966084, upload-time = "2026-01-31T23:10:34.502Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/46/6fa4ea94f1ddf969b2ee941290cca6f1bfac92b53c76ae5f44afe17ceb69/numpy-2.4.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c02ef4401a506fb60b411467ad501e1429a3487abca4664871d9ae0b46c8ba32", size = 16899477, upload-time = "2026-01-31T23:10:37.075Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/a1/2a424e162b1a14a5bd860a464ab4e07513916a64ab1683fae262f735ccd2/numpy-2.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2653de5c24910e49c2b106499803124dde62a5a1fe0eedeaecf4309a5f639390", size = 17323429, upload-time = "2026-01-31T23:10:39.704Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/a2/73014149ff250628df72c58204822ac01d768697913881aacf839ff78680/numpy-2.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1ae241bbfc6ae276f94a170b14785e561cb5e7f626b6688cf076af4110887413", size = 18635109, upload-time = "2026-01-31T23:10:41.924Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/0c/73e8be2f1accd56df74abc1c5e18527822067dced5ec0861b5bb882c2ce0/numpy-2.4.2-cp311-cp311-win32.whl", hash = "sha256:df1b10187212b198dd45fa943d8985a3c8cf854aed4923796e0e019e113a1bda", size = 6237915, upload-time = "2026-01-31T23:10:45.26Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/ae/e0265e0163cf127c24c3969d29f1c4c64551a1e375d95a13d32eab25d364/numpy-2.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:b9c618d56a29c9cb1c4da979e9899be7578d2e0b3c24d52079c166324c9e8695", size = 12607972, upload-time = "2026-01-31T23:10:47.021Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/a5/c43029af9b8014d6ea157f192652c50042e8911f4300f8f6ed3336bf437f/numpy-2.4.2-cp311-cp311-win_arm64.whl", hash = "sha256:47c5a6ed21d9452b10227e5e8a0e1c22979811cad7dcc19d8e3e2fb8fa03f1a3", size = 10485763, upload-time = "2026-01-31T23:10:50.087Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/6e/6f394c9c77668153e14d4da83bcc247beb5952f6ead7699a1a2992613bea/numpy-2.4.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:21982668592194c609de53ba4933a7471880ccbaadcc52352694a59ecc860b3a", size = 16667963, upload-time = "2026-01-31T23:10:52.147Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/f8/55483431f2b2fd015ae6ed4fe62288823ce908437ed49db5a03d15151678/numpy-2.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40397bda92382fcec844066efb11f13e1c9a3e2a8e8f318fb72ed8b6db9f60f1", size = 14693571, upload-time = "2026-01-31T23:10:54.789Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/20/18026832b1845cdc82248208dd929ca14c9d8f2bac391f67440707fff27c/numpy-2.4.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b3a24467af63c67829bfaa61eecf18d5432d4f11992688537be59ecd6ad32f5e", size = 5203469, upload-time = "2026-01-31T23:10:57.343Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/33/2eb97c8a77daaba34eaa3fa7241a14ac5f51c46a6bd5911361b644c4a1e2/numpy-2.4.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:805cc8de9fd6e7a22da5aed858e0ab16be5a4db6c873dde1d7451c541553aa27", size = 6550820, upload-time = "2026-01-31T23:10:59.429Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/91/b97fdfd12dc75b02c44e26c6638241cc004d4079a0321a69c62f51470c4c/numpy-2.4.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d82351358ffbcdcd7b686b90742a9b86632d6c1c051016484fa0b326a0a1548", size = 15663067, upload-time = "2026-01-31T23:11:01.291Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/c6/a18e59f3f0b8071cc85cbc8d80cd02d68aa9710170b2553a117203d46936/numpy-2.4.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e35d3e0144137d9fdae62912e869136164534d64a169f86438bc9561b6ad49f", size = 16619782, upload-time = "2026-01-31T23:11:03.669Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/83/9751502164601a79e18847309f5ceec0b1446d7b6aa12305759b72cf98b2/numpy-2.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adb6ed2ad29b9e15321d167d152ee909ec73395901b70936f029c3bc6d7f4460", size = 17013128, upload-time = "2026-01-31T23:11:05.913Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/c4/c4066322256ec740acc1c8923a10047818691d2f8aec254798f3dd90f5f2/numpy-2.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8906e71fd8afcb76580404e2a950caef2685df3d2a57fe82a86ac8d33cc007ba", size = 18345324, upload-time = "2026-01-31T23:11:08.248Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/af/6157aa6da728fa4525a755bfad486ae7e3f76d4c1864138003eb84328497/numpy-2.4.2-cp312-cp312-win32.whl", hash = "sha256:ec055f6dae239a6299cace477b479cca2fc125c5675482daf1dd886933a1076f", size = 5960282, upload-time = "2026-01-31T23:11:10.497Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/0f/7ceaaeaacb40567071e94dbf2c9480c0ae453d5bb4f52bea3892c39dc83c/numpy-2.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:209fae046e62d0ce6435fcfe3b1a10537e858249b3d9b05829e2a05218296a85", size = 12314210, upload-time = "2026-01-31T23:11:12.176Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/a3/56c5c604fae6dd40fa2ed3040d005fca97e91bd320d232ac9931d77ba13c/numpy-2.4.2-cp312-cp312-win_arm64.whl", hash = "sha256:fbde1b0c6e81d56f5dccd95dd4a711d9b95df1ae4009a60887e56b27e8d903fa", size = 10220171, upload-time = "2026-01-31T23:11:14.684Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/22/815b9fe25d1d7ae7d492152adbc7226d3eff731dffc38fe970589fcaaa38/numpy-2.4.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25f2059807faea4b077a2b6837391b5d830864b3543627f381821c646f31a63c", size = 16663696, upload-time = "2026-01-31T23:11:17.516Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/f0/817d03a03f93ba9c6c8993de509277d84e69f9453601915e4a69554102a1/numpy-2.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bd3a7a9f5847d2fb8c2c6d1c862fa109c31a9abeca1a3c2bd5a64572955b2979", size = 14688322, upload-time = "2026-01-31T23:11:19.883Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/b4/f805ab79293c728b9a99438775ce51885fd4f31b76178767cfc718701a39/numpy-2.4.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8e4549f8a3c6d13d55041925e912bfd834285ef1dd64d6bc7d542583355e2e98", size = 5198157, upload-time = "2026-01-31T23:11:22.375Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/09/826e4289844eccdcd64aac27d13b0fd3f32039915dd5b9ba01baae1f436c/numpy-2.4.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:aea4f66ff44dfddf8c2cffd66ba6538c5ec67d389285292fe428cb2c738c8aef", size = 6546330, upload-time = "2026-01-31T23:11:23.958Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/fb/cbfdbfa3057a10aea5422c558ac57538e6acc87ec1669e666d32ac198da7/numpy-2.4.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3cd545784805de05aafe1dde61752ea49a359ccba9760c1e5d1c88a93bbf2b7", size = 15660968, upload-time = "2026-01-31T23:11:25.713Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/dc/46066ce18d01645541f0186877377b9371b8fa8017fa8262002b4ef22612/numpy-2.4.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0d9b7c93578baafcbc5f0b83eaf17b79d345c6f36917ba0c67f45226911d499", size = 16607311, upload-time = "2026-01-31T23:11:28.117Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/d9/4b5adfc39a43fa6bf918c6d544bc60c05236cc2f6339847fc5b35e6cb5b0/numpy-2.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f74f0f7779cc7ae07d1810aab8ac6b1464c3eafb9e283a40da7309d5e6e48fbb", size = 17012850, upload-time = "2026-01-31T23:11:30.888Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/20/adb6e6adde6d0130046e6fdfb7675cc62bc2f6b7b02239a09eb58435753d/numpy-2.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7ac672d699bf36275c035e16b65539931347d68b70667d28984c9fb34e07fa7", size = 18334210, upload-time = "2026-01-31T23:11:33.214Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/0e/0a73b3dff26803a8c02baa76398015ea2a5434d9b8265a7898a6028c1591/numpy-2.4.2-cp313-cp313-win32.whl", hash = "sha256:8e9afaeb0beff068b4d9cd20d322ba0ee1cecfb0b08db145e4ab4dd44a6b5110", size = 5958199, upload-time = "2026-01-31T23:11:35.385Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/bc/6352f343522fcb2c04dbaf94cb30cca6fd32c1a750c06ad6231b4293708c/numpy-2.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:7df2de1e4fba69a51c06c28f5a3de36731eb9639feb8e1cf7e4a7b0daf4cf622", size = 12310848, upload-time = "2026-01-31T23:11:38.001Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/8d/6da186483e308da5da1cc6918ce913dcfe14ffde98e710bfeff2a6158d4e/numpy-2.4.2-cp313-cp313-win_arm64.whl", hash = "sha256:0fece1d1f0a89c16b03442eae5c56dc0be0c7883b5d388e0c03f53019a4bfd71", size = 10221082, upload-time = "2026-01-31T23:11:40.392Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/a1/9510aa43555b44781968935c7548a8926274f815de42ad3997e9e83680dd/numpy-2.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5633c0da313330fd20c484c78cdd3f9b175b55e1a766c4a174230c6b70ad8262", size = 14815866, upload-time = "2026-01-31T23:11:42.495Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/30/6bbb5e76631a5ae46e7923dd16ca9d3f1c93cfa8d4ed79a129814a9d8db3/numpy-2.4.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d9f64d786b3b1dd742c946c42d15b07497ed14af1a1f3ce840cce27daa0ce913", size = 5325631, upload-time = "2026-01-31T23:11:44.7Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/00/3a490938800c1923b567b3a15cd17896e68052e2145d8662aaf3e1ffc58f/numpy-2.4.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:b21041e8cb6a1eb5312dd1d2f80a94d91efffb7a06b70597d44f1bd2dfc315ab", size = 6646254, upload-time = "2026-01-31T23:11:46.341Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/e9/fac0890149898a9b609caa5af7455a948b544746e4b8fe7c212c8edd71f8/numpy-2.4.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00ab83c56211a1d7c07c25e3217ea6695e50a3e2f255053686b081dc0b091a82", size = 15720138, upload-time = "2026-01-31T23:11:48.082Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/5c/08887c54e68e1e28df53709f1893ce92932cc6f01f7c3d4dc952f61ffd4e/numpy-2.4.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fb882da679409066b4603579619341c6d6898fc83a8995199d5249f986e8e8f", size = 16655398, upload-time = "2026-01-31T23:11:50.293Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/89/253db0fa0e66e9129c745e4ef25631dc37d5f1314dad2b53e907b8538e6d/numpy-2.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66cb9422236317f9d44b67b4d18f44efe6e9c7f8794ac0462978513359461554", size = 17079064, upload-time = "2026-01-31T23:11:52.927Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/d5/cbade46ce97c59c6c3da525e8d95b7abe8a42974a1dc5c1d489c10433e88/numpy-2.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0f01dcf33e73d80bd8dc0f20a71303abbafa26a19e23f6b68d1aa9990af90257", size = 18379680, upload-time = "2026-01-31T23:11:55.22Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/62/48f99ae172a4b63d981babe683685030e8a3df4f246c893ea5c6ef99f018/numpy-2.4.2-cp313-cp313t-win32.whl", hash = "sha256:52b913ec40ff7ae845687b0b34d8d93b60cb66dcee06996dd5c99f2fc9328657", size = 6082433, upload-time = "2026-01-31T23:11:58.096Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/38/e054a61cfe48ad9f1ed0d188e78b7e26859d0b60ef21cd9de4897cdb5326/numpy-2.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:5eea80d908b2c1f91486eb95b3fb6fab187e569ec9752ab7d9333d2e66bf2d6b", size = 12451181, upload-time = "2026-01-31T23:11:59.782Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/a4/a05c3a6418575e185dd84d0b9680b6bb2e2dc3e4202f036b7b4e22d6e9dc/numpy-2.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fd49860271d52127d61197bb50b64f58454e9f578cb4b2c001a6de8b1f50b0b1", size = 10290756, upload-time = "2026-01-31T23:12:02.438Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/88/b7df6050bf18fdcfb7046286c6535cabbdd2064a3440fca3f069d319c16e/numpy-2.4.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:444be170853f1f9d528428eceb55f12918e4fda5d8805480f36a002f1415e09b", size = 16663092, upload-time = "2026-01-31T23:12:04.521Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/7a/1fee4329abc705a469a4afe6e69b1ef7e915117747886327104a8493a955/numpy-2.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d1240d50adff70c2a88217698ca844723068533f3f5c5fa6ee2e3220e3bdb000", size = 14698770, upload-time = "2026-01-31T23:12:06.96Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/0b/f9e49ba6c923678ad5bc38181c08ac5e53b7a5754dbca8e581aa1a56b1ff/numpy-2.4.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:7cdde6de52fb6664b00b056341265441192d1291c130e99183ec0d4b110ff8b1", size = 5208562, upload-time = "2026-01-31T23:12:09.632Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/12/d7de8f6f53f9bb76997e5e4c069eda2051e3fe134e9181671c4391677bb2/numpy-2.4.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:cda077c2e5b780200b6b3e09d0b42205a3d1c68f30c6dceb90401c13bff8fe74", size = 6543710, upload-time = "2026-01-31T23:12:11.969Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/63/c66418c2e0268a31a4cf8a8b512685748200f8e8e8ec6c507ce14e773529/numpy-2.4.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d30291931c915b2ab5717c2974bb95ee891a1cf22ebc16a8006bd59cd210d40a", size = 15677205, upload-time = "2026-01-31T23:12:14.33Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/6c/7f237821c9642fb2a04d2f1e88b4295677144ca93285fd76eff3bcba858d/numpy-2.4.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bba37bc29d4d85761deed3954a1bc62be7cf462b9510b51d367b769a8c8df325", size = 16611738, upload-time = "2026-01-31T23:12:16.525Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/a7/39c4cdda9f019b609b5c473899d87abff092fc908cfe4d1ecb2fcff453b0/numpy-2.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b2f0073ed0868db1dcd86e052d37279eef185b9c8db5bf61f30f46adac63c909", size = 17028888, upload-time = "2026-01-31T23:12:19.306Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/b3/e84bb64bdfea967cc10950d71090ec2d84b49bc691df0025dddb7c26e8e3/numpy-2.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7f54844851cdb630ceb623dcec4db3240d1ac13d4990532446761baede94996a", size = 18339556, upload-time = "2026-01-31T23:12:21.816Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/f5/954a291bc1192a27081706862ac62bb5920fbecfbaa302f64682aa90beed/numpy-2.4.2-cp314-cp314-win32.whl", hash = "sha256:12e26134a0331d8dbd9351620f037ec470b7c75929cb8a1537f6bfe411152a1a", size = 6006899, upload-time = "2026-01-31T23:12:24.14Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/cb/eff72a91b2efdd1bc98b3b8759f6a1654aa87612fc86e3d87d6fe4f948c4/numpy-2.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:068cdb2d0d644cdb45670810894f6a0600797a69c05f1ac478e8d31670b8ee75", size = 12443072, upload-time = "2026-01-31T23:12:26.33Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/75/62726948db36a56428fce4ba80a115716dc4fad6a3a4352487f8bb950966/numpy-2.4.2-cp314-cp314-win_arm64.whl", hash = "sha256:6ed0be1ee58eef41231a5c943d7d1375f093142702d5723ca2eb07db9b934b05", size = 10494886, upload-time = "2026-01-31T23:12:28.488Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/2f/ee93744f1e0661dc267e4b21940870cabfae187c092e1433b77b09b50ac4/numpy-2.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:98f16a80e917003a12c0580f97b5f875853ebc33e2eaa4bccfc8201ac6869308", size = 14818567, upload-time = "2026-01-31T23:12:30.709Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/24/6535212add7d76ff938d8bdc654f53f88d35cddedf807a599e180dcb8e66/numpy-2.4.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:20abd069b9cda45874498b245c8015b18ace6de8546bf50dfa8cea1696ed06ef", size = 5328372, upload-time = "2026-01-31T23:12:32.962Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/9d/c48f0a035725f925634bf6b8994253b43f2047f6778a54147d7e213bc5a7/numpy-2.4.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:e98c97502435b53741540a5717a6749ac2ada901056c7db951d33e11c885cc7d", size = 6649306, upload-time = "2026-01-31T23:12:34.797Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/05/7c73a9574cd4a53a25907bad38b59ac83919c0ddc8234ec157f344d57d9a/numpy-2.4.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da6cad4e82cb893db4b69105c604d805e0c3ce11501a55b5e9f9083b47d2ffe8", size = 15722394, upload-time = "2026-01-31T23:12:36.565Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/fa/4de10089f21fc7d18442c4a767ab156b25c2a6eaf187c0db6d9ecdaeb43f/numpy-2.4.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4424677ce4b47fe73c8b5556d876571f7c6945d264201180db2dc34f676ab5", size = 16653343, upload-time = "2026-01-31T23:12:39.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/f9/d33e4ffc857f3763a57aa85650f2e82486832d7492280ac21ba9efda80da/numpy-2.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2b8f157c8a6f20eb657e240f8985cc135598b2b46985c5bccbde7616dc9c6b1e", size = 17078045, upload-time = "2026-01-31T23:12:42.041Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/b8/54bdb43b6225badbea6389fa038c4ef868c44f5890f95dd530a218706da3/numpy-2.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5daf6f3914a733336dab21a05cdec343144600e964d2fcdabaac0c0269874b2a", size = 18380024, upload-time = "2026-01-31T23:12:44.331Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/55/6e1a61ded7af8df04016d81b5b02daa59f2ea9252ee0397cb9f631efe9e5/numpy-2.4.2-cp314-cp314t-win32.whl", hash = "sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443", size = 6153937, upload-time = "2026-01-31T23:12:47.229Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/aa/fa6118d1ed6d776b0983f3ceac9b1a5558e80df9365b1c3aa6d42bf9eee4/numpy-2.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236", size = 12631844, upload-time = "2026-01-31T23:12:48.997Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/0a/2ec5deea6dcd158f254a7b372fb09cfba5719419c8d66343bab35237b3fb/numpy-2.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181", size = 10565379, upload-time = "2026-01-31T23:12:51.345Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/f8/50e14d36d915ef64d8f8bc4a087fc8264d82c785eda6711f80ab7e620335/numpy-2.4.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:89f7268c009bc492f506abd6f5265defa7cb3f7487dc21d357c3d290add45082", size = 16833179, upload-time = "2026-01-31T23:12:53.5Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/17/809b5cad63812058a8189e91a1e2d55a5a18fd04611dbad244e8aeae465c/numpy-2.4.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6dee3bb76aa4009d5a912180bf5b2de012532998d094acee25d9cb8dee3e44a", size = 14889755, upload-time = "2026-01-31T23:12:55.933Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/ea/181b9bcf7627fc8371720316c24db888dcb9829b1c0270abf3d288b2e29b/numpy-2.4.2-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:cd2bd2bbed13e213d6b55dc1d035a4f91748a7d3edc9480c13898b0353708920", size = 5399500, upload-time = "2026-01-31T23:12:58.671Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/9f/413adf3fc955541ff5536b78fcf0754680b3c6d95103230252a2c9408d23/numpy-2.4.2-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:cf28c0c1d4c4bf00f509fa7eb02c58d7caf221b50b467bcb0d9bbf1584d5c821", size = 6714252, upload-time = "2026-01-31T23:13:00.518Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/da/643aad274e29ccbdf42ecd94dafe524b81c87bcb56b83872d54827f10543/numpy-2.4.2-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e04ae107ac591763a47398bb45b568fc38f02dbc4aa44c063f67a131f99346cb", size = 15797142, upload-time = "2026-01-31T23:13:02.219Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/27/965b8525e9cb5dc16481b30a1b3c21e50c7ebf6e9dbd48d0c4d0d5089c7e/numpy-2.4.2-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:602f65afdef699cda27ec0b9224ae5dc43e328f4c24c689deaf77133dbee74d0", size = 16727979, upload-time = "2026-01-31T23:13:04.62Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/e5/b7d20451657664b07986c2f6e3be564433f5dcaf3482d68eaecd79afaf03/numpy-2.4.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be71bf1edb48ebbbf7f6337b5bfd2f895d1902f6335a5830b20141fc126ffba0", size = 12502577, upload-time = "2026-01-31T23:13:07.08Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opencv-contrib-python"
|
||||
version = "4.13.0.92"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||
{ name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/4c/a45c96b9fe90b2c48ee604f5176eb7deb46ce7c2e87c8d819d2945dbcab6/opencv_contrib_python-4.13.0.92-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:53c8ab81376210dda5836307eb6bda7266f39a3820a9a070c7131510ba815fe1", size = 52041546, upload-time = "2026-02-05T07:01:29.918Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/6c/ba1f3177927deeb3002b62fb8db89daea3b5dc732d61de5bf4c73ed6ebf7/opencv_contrib_python-4.13.0.92-cp37-abi3-macosx_14_0_x86_64.whl", hash = "sha256:1973d0fc773873f9d1b5bf0d1b65895da2f47b06ba033b7d58393f5c28ba0778", size = 38830319, upload-time = "2026-02-05T07:01:47.222Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/7a/fe87eaf109b454af4a2579f46958b3cafb0f804b9c788c108760723a9bb7/opencv_contrib_python-4.13.0.92-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f9cb522dd9e465dfca3536c15288f7936b9827432fb9c885eaf94dc5f88c2a3", size = 53339457, upload-time = "2026-02-05T10:09:02.332Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/27/3665ca4b75ddfd218f9ab139f0463d9571e87aaf59391d3c4f5546c08df7/opencv_contrib_python-4.13.0.92-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f9ceec5886419860a31b518991a99e978e5a6a78dca1470103ad4ede0155f156", size = 76591184, upload-time = "2026-02-05T10:11:51.298Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/11/10c46e9527c4591d5264117debd8fe0e21bb23dbf378ce760add6b1e85b6/opencv_contrib_python-4.13.0.92-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a3c54377c5cf9c45d9b1a207df26dc8fe4f1042d07036cb17d80930c04b25d97", size = 52544155, upload-time = "2026-02-05T10:13:32.068Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/f6/3c645c21358079097201090de7c30d110f5ec3fa01008e3ee81b0a77a354/opencv_contrib_python-4.13.0.92-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:fc5ee50e2be9d40e913536f7f20cc6f87f25d8e413ebb32a3335ab6edf245d3e", size = 79150872, upload-time = "2026-02-05T10:16:03.465Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/d7/bf4622e0ed8a93f5a685c76933e287477cf185a160c66478cf144fece489/opencv_contrib_python-4.13.0.92-cp37-abi3-win32.whl", hash = "sha256:f5d02357f4d5575c300eab3ec1c7ecfed3a9a53e55a76927bab7cfc9e0a67b68", size = 36829959, upload-time = "2026-02-05T07:02:22.25Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/98/a03f69ff6fb86a67d584ecc990d85a95e6930b96e3f39ad1f8e019cb8ada/opencv_contrib_python-4.13.0.92-cp37-abi3-win_amd64.whl", hash = "sha256:cb694dcf76bb2c8d7fa573fc1a99339e8b6640194d7778381e74cc3445369e45", size = 46486178, upload-time = "2026-02-05T07:02:19.551Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opencv-python"
|
||||
version = "4.13.0.92"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||
{ name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/6f/5a28fef4c4a382be06afe3938c64cc168223016fa520c5abaf37e8862aa5/opencv_python-4.13.0.92-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:caf60c071ec391ba51ed00a4a920f996d0b64e3e46068aac1f646b5de0326a19", size = 46247052, upload-time = "2026-02-05T07:01:25.046Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/ac/6c98c44c650b8114a0fb901691351cfb3956d502e8e9b5cd27f4ee7fbf2f/opencv_python-4.13.0.92-cp37-abi3-macosx_14_0_x86_64.whl", hash = "sha256:5868a8c028a0b37561579bfb8ac1875babdc69546d236249fff296a8c010ccf9", size = 32568781, upload-time = "2026-02-05T07:01:41.379Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/51/82fed528b45173bf629fa44effb76dff8bc9f4eeaee759038362dfa60237/opencv_python-4.13.0.92-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0bc2596e68f972ca452d80f444bc404e08807d021fbba40df26b61b18e01838a", size = 47685527, upload-time = "2026-02-05T06:59:11.24Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/07/90b34a8e2cf9c50fe8ed25cac9011cde0676b4d9d9c973751ac7616223a2/opencv_python-4.13.0.92-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:402033cddf9d294693094de5ef532339f14ce821da3ad7df7c9f6e8316da32cf", size = 70460872, upload-time = "2026-02-05T06:59:19.162Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/6d/7a9cc719b3eaf4377b9c2e3edeb7ed3a81de41f96421510c0a169ca3cfd4/opencv_python-4.13.0.92-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:bccaabf9eb7f897ca61880ce2869dcd9b25b72129c28478e7f2a5e8dee945616", size = 46708208, upload-time = "2026-02-05T06:59:15.419Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/55/b3b49a1b97aabcfbbd6c7326df9cb0b6fa0c0aefa8e89d500939e04aa229/opencv_python-4.13.0.92-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:620d602b8f7d8b8dab5f4b99c6eb353e78d3fb8b0f53db1bd258bb1aa001c1d5", size = 72927042, upload-time = "2026-02-05T06:59:23.389Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/17/de5458312bcb07ddf434d7bfcb24bb52c59635ad58c6e7c751b48949b009/opencv_python-4.13.0.92-cp37-abi3-win32.whl", hash = "sha256:372fe164a3148ac1ca51e5f3ad0541a4a276452273f503441d718fab9c5e5f59", size = 30932638, upload-time = "2026-02-05T07:02:14.98Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/a5/1be1516390333ff9be3a9cb648c9f33df79d5096e5884b5df71a588af463/opencv_python-4.13.0.92-cp37-abi3-win_amd64.whl", hash = "sha256:423d934c9fafb91aad38edf26efb46da91ffbc05f3f59c4b0c72e699720706f5", size = 40212062, upload-time = "2026-02-05T07:02:12.724Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pillow"
|
||||
version = "12.1.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1f/42/5c74462b4fd957fcd7b13b04fb3205ff8349236ea74c7c375766d6c82288/pillow-12.1.1.tar.gz", hash = "sha256:9ad8fa5937ab05218e2b6a4cff30295ad35afd2f83ac592e68c0d871bb0fdbc4", size = 46980264, upload-time = "2026-02-11T04:23:07.146Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/30/5bd3d794762481f8c8ae9c80e7b76ecea73b916959eb587521358ef0b2f9/pillow-12.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f1625b72740fdda5d77b4def688eb8fd6490975d06b909fd19f13f391e077e0", size = 5304099, upload-time = "2026-02-11T04:20:06.13Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/c1/aab9e8f3eeb4490180e357955e15c2ef74b31f64790ff356c06fb6cf6d84/pillow-12.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:178aa072084bd88ec759052feca8e56cbb14a60b39322b99a049e58090479713", size = 4657880, upload-time = "2026-02-11T04:20:09.291Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/0a/9879e30d56815ad529d3985aeff5af4964202425c27261a6ada10f7cbf53/pillow-12.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b66e95d05ba806247aaa1561f080abc7975daf715c30780ff92a20e4ec546e1b", size = 6222587, upload-time = "2026-02-11T04:20:10.82Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/5f/a1b72ff7139e4f89014e8d451442c74a774d5c43cd938fb0a9f878576b37/pillow-12.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89c7e895002bbe49cdc5426150377cbbc04767d7547ed145473f496dfa40408b", size = 8027678, upload-time = "2026-02-11T04:20:12.455Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/c2/c7cb187dac79a3d22c3ebeae727abee01e077c8c7d930791dc592f335153/pillow-12.1.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a5cbdcddad0af3da87cb16b60d23648bc3b51967eb07223e9fed77a82b457c4", size = 6335777, upload-time = "2026-02-11T04:20:14.441Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/7b/f9b09a7804ec7336effb96c26d37c29d27225783dc1501b7d62dcef6ae25/pillow-12.1.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f51079765661884a486727f0729d29054242f74b46186026582b4e4769918e4", size = 7027140, upload-time = "2026-02-11T04:20:16.387Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/b2/2fa3c391550bd421b10849d1a2144c44abcd966daadd2f7c12e19ea988c4/pillow-12.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:99c1506ea77c11531d75e3a412832a13a71c7ebc8192ab9e4b2e355555920e3e", size = 6449855, upload-time = "2026-02-11T04:20:18.554Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/ff/9caf4b5b950c669263c39e96c78c0d74a342c71c4f43fd031bb5cb7ceac9/pillow-12.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36341d06738a9f66c8287cf8b876d24b18db9bd8740fa0672c74e259ad408cff", size = 7151329, upload-time = "2026-02-11T04:20:20.646Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/f8/4b24841f582704da675ca535935bccb32b00a6da1226820845fac4a71136/pillow-12.1.1-cp310-cp310-win32.whl", hash = "sha256:6c52f062424c523d6c4db85518774cc3d50f5539dd6eed32b8f6229b26f24d40", size = 6325574, upload-time = "2026-02-11T04:20:22.43Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/f9/9f6b01c0881d7036063aa6612ef04c0e2cad96be21325a1e92d0203f8e91/pillow-12.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6008de247150668a705a6338156efb92334113421ceecf7438a12c9a12dab23", size = 7032347, upload-time = "2026-02-11T04:20:23.932Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/13/c7922edded3dcdaf10c59297540b72785620abc0538872c819915746757d/pillow-12.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:1a9b0ee305220b392e1124a764ee4265bd063e54a751a6b62eff69992f457fa9", size = 2453457, upload-time = "2026-02-11T04:20:25.392Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/46/5da1ec4a5171ee7bf1a0efa064aba70ba3d6e0788ce3f5acd1375d23c8c0/pillow-12.1.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e879bb6cd5c73848ef3b2b48b8af9ff08c5b71ecda8048b7dd22d8a33f60be32", size = 5304084, upload-time = "2026-02-11T04:20:27.501Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/93/a29e9bc02d1cf557a834da780ceccd54e02421627200696fcf805ebdc3fb/pillow-12.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:365b10bb9417dd4498c0e3b128018c4a624dc11c7b97d8cc54effe3b096f4c38", size = 4657866, upload-time = "2026-02-11T04:20:29.827Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/84/583a4558d492a179d31e4aae32eadce94b9acf49c0337c4ce0b70e0a01f2/pillow-12.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d4ce8e329c93845720cd2014659ca67eac35f6433fd3050393d85f3ecef0dad5", size = 6232148, upload-time = "2026-02-11T04:20:31.329Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/e2/53c43334bbbb2d3b938978532fbda8e62bb6e0b23a26ce8592f36bcc4987/pillow-12.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc354a04072b765eccf2204f588a7a532c9511e8b9c7f900e1b64e3e33487090", size = 8038007, upload-time = "2026-02-11T04:20:34.225Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/a6/3d0e79c8a9d58150dd98e199d7c1c56861027f3829a3a60b3c2784190180/pillow-12.1.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e7976bf1910a8116b523b9f9f58bf410f3e8aa330cd9a2bb2953f9266ab49af", size = 6345418, upload-time = "2026-02-11T04:20:35.858Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/c8/46dfeac5825e600579157eea177be43e2f7ff4a99da9d0d0a49533509ac5/pillow-12.1.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:597bd9c8419bc7c6af5604e55847789b69123bbe25d65cc6ad3012b4f3c98d8b", size = 7034590, upload-time = "2026-02-11T04:20:37.91Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/bf/e6f65d3db8a8bbfeaf9e13cc0417813f6319863a73de934f14b2229ada18/pillow-12.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c1fc0f2ca5f96a3c8407e41cca26a16e46b21060fe6d5b099d2cb01412222f5", size = 6458655, upload-time = "2026-02-11T04:20:39.496Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/c2/66091f3f34a25894ca129362e510b956ef26f8fb67a0e6417bc5744e56f1/pillow-12.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:578510d88c6229d735855e1f278aa305270438d36a05031dfaae5067cc8eb04d", size = 7159286, upload-time = "2026-02-11T04:20:41.139Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/5a/24bc8eb526a22f957d0cec6243146744966d40857e3d8deb68f7902ca6c1/pillow-12.1.1-cp311-cp311-win32.whl", hash = "sha256:7311c0a0dcadb89b36b7025dfd8326ecfa36964e29913074d47382706e516a7c", size = 6328663, upload-time = "2026-02-11T04:20:43.184Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/03/bef822e4f2d8f9d7448c133d0a18185d3cce3e70472774fffefe8b0ed562/pillow-12.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:fbfa2a7c10cc2623f412753cddf391c7f971c52ca40a3f65dc5039b2939e8563", size = 7031448, upload-time = "2026-02-11T04:20:44.696Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/70/f76296f53610bd17b2e7d31728b8b7825e3ac3b5b3688b51f52eab7c0818/pillow-12.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:b81b5e3511211631b3f672a595e3221252c90af017e399056d0faabb9538aa80", size = 2453651, upload-time = "2026-02-11T04:20:46.243Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/d3/8df65da0d4df36b094351dce696f2989bec731d4f10e743b1c5f4da4d3bf/pillow-12.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab323b787d6e18b3d91a72fc99b1a2c28651e4358749842b8f8dfacd28ef2052", size = 5262803, upload-time = "2026-02-11T04:20:47.653Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/71/5026395b290ff404b836e636f51d7297e6c83beceaa87c592718747e670f/pillow-12.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:adebb5bee0f0af4909c30db0d890c773d1a92ffe83da908e2e9e720f8edf3984", size = 4657601, upload-time = "2026-02-11T04:20:49.328Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/2e/1001613d941c67442f745aff0f7cc66dd8df9a9c084eb497e6a543ee6f7e/pillow-12.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb66b7cc26f50977108790e2456b7921e773f23db5630261102233eb355a3b79", size = 6234995, upload-time = "2026-02-11T04:20:51.032Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/26/246ab11455b2549b9233dbd44d358d033a2f780fa9007b61a913c5b2d24e/pillow-12.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aee2810642b2898bb187ced9b349e95d2a7272930796e022efaf12e99dccd293", size = 8045012, upload-time = "2026-02-11T04:20:52.882Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/8b/07587069c27be7535ac1fe33874e32de118fbd34e2a73b7f83436a88368c/pillow-12.1.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a0b1cd6232e2b618adcc54d9882e4e662a089d5768cd188f7c245b4c8c44a397", size = 6349638, upload-time = "2026-02-11T04:20:54.444Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/79/6df7b2ee763d619cda2fb4fea498e5f79d984dae304d45a8999b80d6cf5c/pillow-12.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7aac39bcf8d4770d089588a2e1dd111cbaa42df5a94be3114222057d68336bd0", size = 7041540, upload-time = "2026-02-11T04:20:55.97Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/5e/2ba19e7e7236d7529f4d873bdaf317a318896bac289abebd4bb00ef247f0/pillow-12.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ab174cd7d29a62dd139c44bf74b698039328f45cb03b4596c43473a46656b2f3", size = 6462613, upload-time = "2026-02-11T04:20:57.542Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/03/31216ec124bb5c3dacd74ce8efff4cc7f52643653bad4825f8f08c697743/pillow-12.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:339ffdcb7cbeaa08221cd401d517d4b1fe7a9ed5d400e4a8039719238620ca35", size = 7166745, upload-time = "2026-02-11T04:20:59.196Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/e7/7c4552d80052337eb28653b617eafdef39adfb137c49dd7e831b8dc13bc5/pillow-12.1.1-cp312-cp312-win32.whl", hash = "sha256:5d1f9575a12bed9e9eedd9a4972834b08c97a352bd17955ccdebfeca5913fa0a", size = 6328823, upload-time = "2026-02-11T04:21:01.385Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/17/688626d192d7261bbbf98846fc98995726bddc2c945344b65bec3a29d731/pillow-12.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:21329ec8c96c6e979cd0dfd29406c40c1d52521a90544463057d2aaa937d66a6", size = 7033367, upload-time = "2026-02-11T04:21:03.536Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/fe/a0ef1f73f939b0eca03ee2c108d0043a87468664770612602c63266a43c4/pillow-12.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:af9a332e572978f0218686636610555ae3defd1633597be015ed50289a03c523", size = 2453811, upload-time = "2026-02-11T04:21:05.116Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/11/6db24d4bd7685583caeae54b7009584e38da3c3d4488ed4cd25b439de486/pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d242e8ac078781f1de88bf823d70c1a9b3c7950a44cdf4b7c012e22ccbcd8e4e", size = 4062689, upload-time = "2026-02-11T04:21:06.804Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/c0/ce6d3b1fe190f0021203e0d9b5b99e57843e345f15f9ef22fcd43842fd21/pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:02f84dfad02693676692746df05b89cf25597560db2857363a208e393429f5e9", size = 4138535, upload-time = "2026-02-11T04:21:08.452Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/c6/d5eb6a4fb32a3f9c21a8c7613ec706534ea1cf9f4b3663e99f0d83f6fca8/pillow-12.1.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:e65498daf4b583091ccbb2556c7000abf0f3349fcd57ef7adc9a84a394ed29f6", size = 3601364, upload-time = "2026-02-11T04:21:10.194Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/a1/16c4b823838ba4c9c52c0e6bbda903a3fe5a1bdbf1b8eb4fff7156f3e318/pillow-12.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c6db3b84c87d48d0088943bf33440e0c42370b99b1c2a7989216f7b42eede60", size = 5262561, upload-time = "2026-02-11T04:21:11.742Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/ad/ad9dc98ff24f485008aa5cdedaf1a219876f6f6c42a4626c08bc4e80b120/pillow-12.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b7e5304e34942bf62e15184219a7b5ad4ff7f3bb5cca4d984f37df1a0e1aee2", size = 4657460, upload-time = "2026-02-11T04:21:13.786Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/1b/f1a4ea9a895b5732152789326202a82464d5254759fbacae4deea3069334/pillow-12.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:18e5bddd742a44b7e6b1e773ab5db102bd7a94c32555ba656e76d319d19c3850", size = 6232698, upload-time = "2026-02-11T04:21:15.949Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/f4/86f51b8745070daf21fd2e5b1fe0eb35d4db9ca26e6d58366562fb56a743/pillow-12.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc44ef1f3de4f45b50ccf9136999d71abb99dca7706bc75d222ed350b9fd2289", size = 8041706, upload-time = "2026-02-11T04:21:17.723Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/9b/d6ecd956bb1266dd1045e995cce9b8d77759e740953a1c9aad9502a0461e/pillow-12.1.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a8eb7ed8d4198bccbd07058416eeec51686b498e784eda166395a23eb99138e", size = 6346621, upload-time = "2026-02-11T04:21:19.547Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/24/538bff45bde96535d7d998c6fed1a751c75ac7c53c37c90dc2601b243893/pillow-12.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47b94983da0c642de92ced1702c5b6c292a84bd3a8e1d1702ff923f183594717", size = 7038069, upload-time = "2026-02-11T04:21:21.378Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/0e/58cb1a6bc48f746bc4cb3adb8cabff73e2742c92b3bf7a220b7cf69b9177/pillow-12.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:518a48c2aab7ce596d3bf79d0e275661b846e86e4d0e7dec34712c30fe07f02a", size = 6460040, upload-time = "2026-02-11T04:21:23.148Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/57/9045cb3ff11eeb6c1adce3b2d60d7d299d7b273a2e6c8381a524abfdc474/pillow-12.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a550ae29b95c6dc13cf69e2c9dc5747f814c54eeb2e32d683e5e93af56caa029", size = 7164523, upload-time = "2026-02-11T04:21:25.01Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/f2/9be9cb99f2175f0d4dbadd6616ce1bf068ee54a28277ea1bf1fbf729c250/pillow-12.1.1-cp313-cp313-win32.whl", hash = "sha256:a003d7422449f6d1e3a34e3dd4110c22148336918ddbfc6a32581cd54b2e0b2b", size = 6332552, upload-time = "2026-02-11T04:21:27.238Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/eb/b0834ad8b583d7d9d42b80becff092082a1c3c156bb582590fcc973f1c7c/pillow-12.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:344cf1e3dab3be4b1fa08e449323d98a2a3f819ad20f4b22e77a0ede31f0faa1", size = 7040108, upload-time = "2026-02-11T04:21:29.462Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/7d/fc09634e2aabdd0feabaff4a32f4a7d97789223e7c2042fd805ea4b4d2c2/pillow-12.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c0dd1636633e7e6a0afe7bf6a51a14992b7f8e60de5789018ebbdfae55b040a", size = 2453712, upload-time = "2026-02-11T04:21:31.072Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/2a/b9d62794fc8a0dd14c1943df68347badbd5511103e0d04c035ffe5cf2255/pillow-12.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0330d233c1a0ead844fc097a7d16c0abff4c12e856c0b325f231820fee1f39da", size = 5264880, upload-time = "2026-02-11T04:21:32.865Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/9d/e03d857d1347fa5ed9247e123fcd2a97b6220e15e9cb73ca0a8d91702c6e/pillow-12.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dae5f21afb91322f2ff791895ddd8889e5e947ff59f71b46041c8ce6db790bc", size = 4660616, upload-time = "2026-02-11T04:21:34.97Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/ec/8a6d22afd02570d30954e043f09c32772bfe143ba9285e2fdb11284952cd/pillow-12.1.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e0c664be47252947d870ac0d327fea7e63985a08794758aa8af5b6cb6ec0c9c", size = 6269008, upload-time = "2026-02-11T04:21:36.623Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/1d/6d875422c9f28a4a361f495a5f68d9de4a66941dc2c619103ca335fa6446/pillow-12.1.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:691ab2ac363b8217f7d31b3497108fb1f50faab2f75dfb03284ec2f217e87bf8", size = 8073226, upload-time = "2026-02-11T04:21:38.585Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/cd/134b0b6ee5eda6dc09e25e24b40fdafe11a520bc725c1d0bbaa5e00bf95b/pillow-12.1.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9e8064fb1cc019296958595f6db671fba95209e3ceb0c4734c9baf97de04b20", size = 6380136, upload-time = "2026-02-11T04:21:40.562Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/a9/7628f013f18f001c1b98d8fffe3452f306a70dc6aba7d931019e0492f45e/pillow-12.1.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:472a8d7ded663e6162dafdf20015c486a7009483ca671cece7a9279b512fcb13", size = 7067129, upload-time = "2026-02-11T04:21:42.521Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/f8/66ab30a2193b277785601e82ee2d49f68ea575d9637e5e234faaa98efa4c/pillow-12.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:89b54027a766529136a06cfebeecb3a04900397a3590fd252160b888479517bf", size = 6491807, upload-time = "2026-02-11T04:21:44.22Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/0b/a877a6627dc8318fdb84e357c5e1a758c0941ab1ddffdafd231983788579/pillow-12.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:86172b0831b82ce4f7877f280055892b31179e1576aa00d0df3bb1bbf8c3e524", size = 7190954, upload-time = "2026-02-11T04:21:46.114Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/43/6f732ff85743cf746b1361b91665d9f5155e1483817f693f8d57ea93147f/pillow-12.1.1-cp313-cp313t-win32.whl", hash = "sha256:44ce27545b6efcf0fdbdceb31c9a5bdea9333e664cda58a7e674bb74608b3986", size = 6336441, upload-time = "2026-02-11T04:21:48.22Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/44/e865ef3986611bb75bfabdf94a590016ea327833f434558801122979cd0e/pillow-12.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a285e3eb7a5a45a2ff504e31f4a8d1b12ef62e84e5411c6804a42197c1cf586c", size = 7045383, upload-time = "2026-02-11T04:21:50.015Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/c6/f4fb24268d0c6908b9f04143697ea18b0379490cb74ba9e8d41b898bd005/pillow-12.1.1-cp313-cp313t-win_arm64.whl", hash = "sha256:cc7d296b5ea4d29e6570dabeaed58d31c3fea35a633a69679fb03d7664f43fb3", size = 2456104, upload-time = "2026-02-11T04:21:51.633Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/d0/bebb3ffbf31c5a8e97241476c4cf8b9828954693ce6744b4a2326af3e16b/pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:417423db963cb4be8bac3fc1204fe61610f6abeed1580a7a2cbb2fbda20f12af", size = 4062652, upload-time = "2026-02-11T04:21:53.19Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/c0/0e16fb0addda4851445c28f8350d8c512f09de27bbb0d6d0bbf8b6709605/pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:b957b71c6b2387610f556a7eb0828afbe40b4a98036fc0d2acfa5a44a0c2036f", size = 4138823, upload-time = "2026-02-11T04:22:03.088Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/fb/6170ec655d6f6bb6630a013dd7cf7bc218423d7b5fa9071bf63dc32175ae/pillow-12.1.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:097690ba1f2efdeb165a20469d59d8bb03c55fb6621eb2041a060ae8ea3e9642", size = 3601143, upload-time = "2026-02-11T04:22:04.909Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/04/dc5c3f297510ba9a6837cbb318b87dd2b8f73eb41a43cc63767f65cb599c/pillow-12.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2815a87ab27848db0321fb78c7f0b2c8649dee134b7f2b80c6a45c6831d75ccd", size = 5266254, upload-time = "2026-02-11T04:22:07.656Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/30/5db1236b0d6313f03ebf97f5e17cda9ca060f524b2fcc875149a8360b21c/pillow-12.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f7ed2c6543bad5a7d5530eb9e78c53132f93dfa44a28492db88b41cdab885202", size = 4657499, upload-time = "2026-02-11T04:22:09.613Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/18/008d2ca0eb612e81968e8be0bbae5051efba24d52debf930126d7eaacbba/pillow-12.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:652a2c9ccfb556235b2b501a3a7cf3742148cd22e04b5625c5fe057ea3e3191f", size = 6232137, upload-time = "2026-02-11T04:22:11.434Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/f1/f14d5b8eeb4b2cd62b9f9f847eb6605f103df89ef619ac68f92f748614ea/pillow-12.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d6e4571eedf43af33d0fc233a382a76e849badbccdf1ac438841308652a08e1f", size = 8042721, upload-time = "2026-02-11T04:22:13.321Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/d6/17824509146e4babbdabf04d8171491fa9d776f7061ff6e727522df9bd03/pillow-12.1.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b574c51cf7d5d62e9be37ba446224b59a2da26dc4c1bb2ecbe936a4fb1a7cb7f", size = 6347798, upload-time = "2026-02-11T04:22:15.449Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/ee/c85a38a9ab92037a75615aba572c85ea51e605265036e00c5b67dfafbfe2/pillow-12.1.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a37691702ed687799de29a518d63d4682d9016932db66d4e90c345831b02fb4e", size = 7039315, upload-time = "2026-02-11T04:22:17.24Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/f3/bc8ccc6e08a148290d7523bde4d9a0d6c981db34631390dc6e6ec34cacf6/pillow-12.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f95c00d5d6700b2b890479664a06e754974848afaae5e21beb4d83c106923fd0", size = 6462360, upload-time = "2026-02-11T04:22:19.111Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/ab/69a42656adb1d0665ab051eec58a41f169ad295cf81ad45406963105408f/pillow-12.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:559b38da23606e68681337ad74622c4dbba02254fc9cb4488a305dd5975c7eeb", size = 7165438, upload-time = "2026-02-11T04:22:21.041Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/46/81f7aa8941873f0f01d4b55cc543b0a3d03ec2ee30d617a0448bf6bd6dec/pillow-12.1.1-cp314-cp314-win32.whl", hash = "sha256:03edcc34d688572014ff223c125a3f77fb08091e4607e7745002fc214070b35f", size = 6431503, upload-time = "2026-02-11T04:22:22.833Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/72/4c245f7d1044b67affc7f134a09ea619d4895333d35322b775b928180044/pillow-12.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:50480dcd74fa63b8e78235957d302d98d98d82ccbfac4c7e12108ba9ecbdba15", size = 7176748, upload-time = "2026-02-11T04:22:24.64Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/ad/8a87bdbe038c5c698736e3348af5c2194ffb872ea52f11894c95f9305435/pillow-12.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:5cb1785d97b0c3d1d1a16bc1d710c4a0049daefc4935f3a8f31f827f4d3d2e7f", size = 2544314, upload-time = "2026-02-11T04:22:26.685Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/9d/efd18493f9de13b87ede7c47e69184b9e859e4427225ea962e32e56a49bc/pillow-12.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1f90cff8aa76835cba5769f0b3121a22bd4eb9e6884cfe338216e557a9a548b8", size = 5268612, upload-time = "2026-02-11T04:22:29.884Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/f1/4f42eb2b388eb2ffc660dcb7f7b556c1015c53ebd5f7f754965ef997585b/pillow-12.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1f1be78ce9466a7ee64bfda57bdba0f7cc499d9794d518b854816c41bf0aa4e9", size = 4660567, upload-time = "2026-02-11T04:22:31.799Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/01/54/df6ef130fa43e4b82e32624a7b821a2be1c5653a5fdad8469687a7db4e00/pillow-12.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:42fc1f4677106188ad9a55562bbade416f8b55456f522430fadab3cef7cd4e60", size = 6269951, upload-time = "2026-02-11T04:22:33.921Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/48/618752d06cc44bb4aae8ce0cd4e6426871929ed7b46215638088270d9b34/pillow-12.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98edb152429ab62a1818039744d8fbb3ccab98a7c29fc3d5fcef158f3f1f68b7", size = 8074769, upload-time = "2026-02-11T04:22:35.877Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/bd/f1d71eb39a72fa088d938655afba3e00b38018d052752f435838961127d8/pillow-12.1.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d470ab1178551dd17fdba0fef463359c41aaa613cdcd7ff8373f54be629f9f8f", size = 6381358, upload-time = "2026-02-11T04:22:37.698Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/ef/c784e20b96674ed36a5af839305f55616f8b4f8aa8eeccf8531a6e312243/pillow-12.1.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6408a7b064595afcab0a49393a413732a35788f2a5092fdc6266952ed67de586", size = 7068558, upload-time = "2026-02-11T04:22:39.597Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/cb/8059688b74422ae61278202c4e1ad992e8a2e7375227be0a21c6b87ca8d5/pillow-12.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5d8c41325b382c07799a3682c1c258469ea2ff97103c53717b7893862d0c98ce", size = 6493028, upload-time = "2026-02-11T04:22:42.73Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/da/e3c008ed7d2dd1f905b15949325934510b9d1931e5df999bb15972756818/pillow-12.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7697918b5be27424e9ce568193efd13d925c4481dd364e43f5dff72d33e10f8", size = 7191940, upload-time = "2026-02-11T04:22:44.543Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/01/4a/9202e8d11714c1fc5951f2e1ef362f2d7fbc595e1f6717971d5dd750e969/pillow-12.1.1-cp314-cp314t-win32.whl", hash = "sha256:d2912fd8114fc5545aa3a4b5576512f64c55a03f3ebcca4c10194d593d43ea36", size = 6438736, upload-time = "2026-02-11T04:22:46.347Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/ca/cbce2327eb9885476b3957b2e82eb12c866a8b16ad77392864ad601022ce/pillow-12.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:4ceb838d4bd9dab43e06c363cab2eebf63846d6a4aeaea283bbdfd8f1a8ed58b", size = 7182894, upload-time = "2026-02-11T04:22:48.114Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/d2/de599c95ba0a973b94410477f8bf0b6f0b5e67360eb89bcb1ad365258beb/pillow-12.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:7b03048319bfc6170e93bd60728a1af51d3dd7704935feb228c4d4faab35d334", size = 2546446, upload-time = "2026-02-11T04:22:50.342Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/11/5d43209aa4cb58e0cc80127956ff1796a68b928e6324bbf06ef4db34367b/pillow-12.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:600fd103672b925fe62ed08e0d874ea34d692474df6f4bf7ebe148b30f89f39f", size = 5228606, upload-time = "2026-02-11T04:22:52.106Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/d5/3b005b4e4fda6698b371fa6c21b097d4707585d7db99e98d9b0b87ac612a/pillow-12.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:665e1b916b043cef294bc54d47bf02d87e13f769bc4bc5fa225a24b3a6c5aca9", size = 4622321, upload-time = "2026-02-11T04:22:53.827Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/36/ed3ea2d594356fd8037e5a01f6156c74bc8d92dbb0fa60746cc96cabb6e8/pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:495c302af3aad1ca67420ddd5c7bd480c8867ad173528767d906428057a11f0e", size = 5247579, upload-time = "2026-02-11T04:22:56.094Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/9a/9cc3e029683cf6d20ae5085da0dafc63148e3252c2f13328e553aaa13cfb/pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8fd420ef0c52c88b5a035a0886f367748c72147b2b8f384c9d12656678dfdfa9", size = 6989094, upload-time = "2026-02-11T04:22:58.288Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/98/fc53ab36da80b88df0967896b6c4b4cd948a0dc5aa40a754266aa3ae48b3/pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f975aa7ef9684ce7e2c18a3aa8f8e2106ce1e46b94ab713d156b2898811651d3", size = 5313850, upload-time = "2026-02-11T04:23:00.554Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/02/00fa585abfd9fe9d73e5f6e554dc36cc2b842898cbfc46d70353dae227f8/pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8089c852a56c2966cf18835db62d9b34fef7ba74c726ad943928d494fa7f4735", size = 5963343, upload-time = "2026-02-11T04:23:02.934Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/26/c56ce33ca856e358d27fda9676c055395abddb82c35ac0f593877ed4562e/pillow-12.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:cb9bb857b2d057c6dfc72ac5f3b44836924ba15721882ef103cecb40d002d80e", size = 7029880, upload-time = "2026-02-11T04:23:04.783Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf"
|
||||
version = "7.34.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f2/00/04a2ab36b70a52d0356852979e08b44edde0435f2115dc66e25f2100f3ab/protobuf-7.34.0.tar.gz", hash = "sha256:3871a3df67c710aaf7bb8d214cc997342e63ceebd940c8c7fc65c9b3d697591a", size = 454726, upload-time = "2026-02-27T00:30:25.421Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/13/c4/6322ab5c8f279c4c358bc14eb8aefc0550b97222a39f04eb3c1af7a830fa/protobuf-7.34.0-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:8e329966799f2c271d5e05e236459fe1cbfdb8755aaa3b0914fa60947ddea408", size = 429248, upload-time = "2026-02-27T00:30:14.924Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/99/b029bbbc61e8937545da5b79aa405ab2d9cf307a728f8c9459ad60d7a481/protobuf-7.34.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:9d7a5005fb96f3c1e64f397f91500b0eb371b28da81296ae73a6b08a5b76cdd6", size = 325753, upload-time = "2026-02-27T00:30:17.247Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/79/09f02671eb75b251c5550a1c48e7b3d4b0623efd7c95a15a50f6f9fc1e2e/protobuf-7.34.0-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:4a72a8ec94e7a9f7ef7fe818ed26d073305f347f8b3b5ba31e22f81fd85fca02", size = 340200, upload-time = "2026-02-27T00:30:18.672Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/57/89727baef7578897af5ed166735ceb315819f1c184da8c3441271dbcfde7/protobuf-7.34.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:964cf977e07f479c0697964e83deda72bcbc75c3badab506fb061b352d991b01", size = 324268, upload-time = "2026-02-27T00:30:20.088Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/3e/38ff2ddee5cc946f575c9d8cc822e34bde205cf61acf8099ad88ef19d7d2/protobuf-7.34.0-cp310-abi3-win32.whl", hash = "sha256:f791ec509707a1d91bd02e07df157e75e4fb9fbdad12a81b7396201ec244e2e3", size = 426628, upload-time = "2026-02-27T00:30:21.555Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/71/7c32eaf34a61a1bae1b62a2ac4ffe09b8d1bb0cf93ad505f42040023db89/protobuf-7.34.0-cp310-abi3-win_amd64.whl", hash = "sha256:9f9079f1dde4e32342ecbd1c118d76367090d4aaa19da78230c38101c5b3dd40", size = 437901, upload-time = "2026-02-27T00:30:22.836Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/e7/14dc9366696dcb53a413449881743426ed289d687bcf3d5aee4726c32ebb/protobuf-7.34.0-py3-none-any.whl", hash = "sha256:e3b914dd77fa33fa06ab2baa97937746ab25695f389869afdf03e81f34e45dc7", size = 170716, upload-time = "2026-02-27T00:30:23.994Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyzbar"
|
||||
version = "0.1.9"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/24/81ebe6a1c00760471a3028a23cbe0b94e5fa2926e5ba47adc895920887bc/pyzbar-0.1.9-py2.py3-none-any.whl", hash = "sha256:4559628b8192feb25766d954b36a3753baaf5c97c03135aec7e4a026036b475d", size = 32560, upload-time = "2022-03-15T14:53:40.637Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/87/7b596730179ddf17857eea33ba820354dd4e1cf941e57f51ffccce26c409/pyzbar-0.1.9-py2.py3-none-win32.whl", hash = "sha256:8f4c5264c9c7c6b9f20d01efc52a4eba1ded47d9ba857a94130afe33703eb518", size = 810633, upload-time = "2022-03-15T14:53:43.446Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/e2/1c6a8e94197612dbdfc51eab8dfb674168829885fac2c4f50ac8366c25ca/pyzbar-0.1.9-py2.py3-none-win_amd64.whl", hash = "sha256:13e3ee5a2f3a545204a285f41814d5c0db571967e8d4af8699a03afc55182a9c", size = 817363, upload-time = "2022-03-15T14:53:46.691Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qrcode"
|
||||
version = "8.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8f/b2/7fc2931bfae0af02d5f53b174e9cf701adbb35f39d69c2af63d4a39f81a9/qrcode-8.2.tar.gz", hash = "sha256:35c3f2a4172b33136ab9f6b3ef1c00260dd2f66f858f24d88418a015f446506c", size = 43317, upload-time = "2025-05-01T15:44:24.726Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/b8/d2d6d731733f51684bbf76bf34dab3b70a9148e8f2cef2bb544fccec681a/qrcode-8.2-py3-none-any.whl", hash = "sha256:16e64e0716c14960108e85d853062c9e8bba5ca8252c0b4d0231b9df4060ff4f", size = 45986, upload-time = "2025-05-01T15:44:22.781Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qreader"
|
||||
version = "1.3.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||
{ name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "opencv-python" },
|
||||
{ name = "pyzbar" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e7/71/388e67daba0d00fc7f6a62c229dea0c89c3949f0944a1829e6330cde0071/qreader-1.3.2.tar.gz", hash = "sha256:f96d2879c9f47f9641da55dc3e1df765cd4d6f1cfbc8b7fa811b70c1e27d4fba", size = 32542366, upload-time = "2022-12-31T16:10:39.539Z" }
|
||||
Reference in New Issue
Block a user