tbr-deal-finder 0.1.5__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tbr_deal_finder-0.3.2/.github/workflows/build-release-dmg.yml +116 -0
- tbr_deal_finder-0.3.2/.github/workflows/build-release-exe.yml +198 -0
- {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/.gitignore +3 -0
- tbr_deal_finder-0.3.2/CHANGELOG.md +111 -0
- tbr_deal_finder-0.3.2/DESIGN.md +113 -0
- tbr_deal_finder-0.3.2/Makefile +143 -0
- tbr_deal_finder-0.3.2/PKG-INFO +104 -0
- tbr_deal_finder-0.3.2/README.md +82 -0
- tbr_deal_finder-0.3.2/assets/icon.ico +0 -0
- tbr_deal_finder-0.3.2/assets/icon.png +0 -0
- tbr_deal_finder-0.3.2/docs/desktop-app.md +133 -0
- tbr_deal_finder-0.3.2/docs/development.md +198 -0
- tbr_deal_finder-0.3.2/docs/python-cli.md +413 -0
- {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/pyproject.toml +14 -2
- tbr_deal_finder-0.3.2/scripts/packaging/create_dmg.sh +96 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/__init__.py +5 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/__main__.py +7 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/book.py +226 -0
- {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/tbr_deal_finder/cli.py +48 -55
- {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/tbr_deal_finder/config.py +23 -5
- tbr_deal_finder-0.3.2/tbr_deal_finder/desktop_updater.py +147 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/gui/__init__.py +0 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/gui/main.py +754 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/gui/pages/__init__.py +1 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/gui/pages/all_books.py +100 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/gui/pages/all_deals.py +63 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/gui/pages/base_book_page.py +290 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/gui/pages/book_details.py +604 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/gui/pages/latest_deals.py +376 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/gui/pages/settings.py +390 -0
- {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/tbr_deal_finder/migrations.py +42 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/owned_books.py +18 -0
- {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/tbr_deal_finder/queries/get_active_deals.sql +1 -1
- {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/tbr_deal_finder/queries/get_deals_found_at.sql +1 -1
- tbr_deal_finder-0.3.2/tbr_deal_finder/queries/latest_unknown_book_sync.sql +5 -0
- {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/tbr_deal_finder/retailer/__init__.py +2 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/retailer/amazon.py +136 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/retailer/amazon_custom_auth.py +79 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/retailer/audible.py +123 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/retailer/chirp.py +264 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/retailer/kindle.py +165 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/retailer/librofm.py +233 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/retailer/models.py +120 -0
- {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/tbr_deal_finder/retailer_deal.py +104 -60
- tbr_deal_finder-0.3.2/tbr_deal_finder/tracked_books.py +407 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/utils.py +128 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder/version_check.py +40 -0
- tbr_deal_finder-0.3.2/tbr_deal_finder.py +7 -0
- {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/uv.lock +819 -1
- tbr_deal_finder-0.1.5/CHANGELOG.md +0 -12
- tbr_deal_finder-0.1.5/PKG-INFO +0 -167
- tbr_deal_finder-0.1.5/README.md +0 -150
- tbr_deal_finder-0.1.5/tbr_deal_finder/__init__.py +0 -9
- tbr_deal_finder-0.1.5/tbr_deal_finder/book.py +0 -116
- tbr_deal_finder-0.1.5/tbr_deal_finder/library_exports.py +0 -155
- tbr_deal_finder-0.1.5/tbr_deal_finder/retailer/audible.py +0 -146
- tbr_deal_finder-0.1.5/tbr_deal_finder/retailer/chirp.py +0 -80
- tbr_deal_finder-0.1.5/tbr_deal_finder/retailer/librofm.py +0 -140
- tbr_deal_finder-0.1.5/tbr_deal_finder/retailer/models.py +0 -37
- tbr_deal_finder-0.1.5/tbr_deal_finder/utils.py +0 -57
- {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/.github/workflows/publish-to-pypi.yaml +0 -0
- {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/.python-version +0 -0
- {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/LICENSE +0 -0
- {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/tbr_deal_finder/queries/latest_deal_last_ran_most_recent_success.sql +0 -0
@@ -0,0 +1,116 @@
|
|
1
|
+
name: Build and Attach DMG to Release
|
2
|
+
|
3
|
+
on:
|
4
|
+
release:
|
5
|
+
types: [created]
|
6
|
+
|
7
|
+
permissions:
|
8
|
+
contents: write
|
9
|
+
|
10
|
+
jobs:
|
11
|
+
build-dmg:
|
12
|
+
runs-on: macos-latest
|
13
|
+
env:
|
14
|
+
HAS_MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE != '' }}
|
15
|
+
HAS_MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD != '' }}
|
16
|
+
VERSION: ${{ github.event.release.tag_name }}
|
17
|
+
|
18
|
+
steps:
|
19
|
+
- name: Checkout code
|
20
|
+
uses: actions/checkout@v4
|
21
|
+
|
22
|
+
- name: Set up Python
|
23
|
+
uses: actions/setup-python@v5
|
24
|
+
with:
|
25
|
+
python-version: '3.13'
|
26
|
+
|
27
|
+
- name: Install uv
|
28
|
+
uses: astral-sh/setup-uv@v5
|
29
|
+
|
30
|
+
- name: Install project dependencies
|
31
|
+
run: |
|
32
|
+
uv sync
|
33
|
+
|
34
|
+
- name: Set up Xcode
|
35
|
+
uses: maxim-lobanov/setup-xcode@v1
|
36
|
+
with:
|
37
|
+
xcode-version: latest-stable
|
38
|
+
|
39
|
+
- name: Set up CocoaPods
|
40
|
+
uses: maxim-lobanov/setup-cocoapods@v1
|
41
|
+
with:
|
42
|
+
version: latest
|
43
|
+
|
44
|
+
- name: Import Code Signing Certificate (if available)
|
45
|
+
if: ${{ env.HAS_MACOS_CERTIFICATE == 'true' && env.HAS_MACOS_CERTIFICATE_PASSWORD == 'true' }}
|
46
|
+
run: |
|
47
|
+
# Create temporary keychain
|
48
|
+
security create-keychain -p "temp_keychain_password" temp.keychain
|
49
|
+
security default-keychain -s temp.keychain
|
50
|
+
security unlock-keychain -p "temp_keychain_password" temp.keychain
|
51
|
+
|
52
|
+
# Import certificate
|
53
|
+
echo "${{ secrets.MACOS_CERTIFICATE }}" | base64 --decode > certificate.p12
|
54
|
+
security import certificate.p12 -k temp.keychain -P "${{ secrets.MACOS_CERTIFICATE_PASSWORD }}" -T /usr/bin/codesign
|
55
|
+
|
56
|
+
# Allow codesign to access the certificate
|
57
|
+
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "temp_keychain_password" temp.keychain
|
58
|
+
|
59
|
+
# Set code signing identity
|
60
|
+
if [ -n "${{ secrets.CODESIGN_IDENTITY }}" ]; then
|
61
|
+
# Use provided identity name
|
62
|
+
echo "CODESIGN_IDENTITY=${{ secrets.CODESIGN_IDENTITY }}" >> $GITHUB_ENV
|
63
|
+
echo "✅ Code signing certificate imported: ${{ secrets.CODESIGN_IDENTITY }}"
|
64
|
+
else
|
65
|
+
# Auto-detect identity from certificate
|
66
|
+
CERT_IDENTITY=$(security find-identity -v -p codesigning temp.keychain | head -1 | grep -o '"[^"]*"' | tr -d '"')
|
67
|
+
echo "CODESIGN_IDENTITY=$CERT_IDENTITY" >> $GITHUB_ENV
|
68
|
+
echo "✅ Code signing certificate imported (auto-detected): $CERT_IDENTITY"
|
69
|
+
fi
|
70
|
+
|
71
|
+
# Clean up certificate file
|
72
|
+
rm -f certificate.p12
|
73
|
+
|
74
|
+
- name: Build DMG
|
75
|
+
run: |
|
76
|
+
make build-mac
|
77
|
+
|
78
|
+
- name: Cleanup Code Signing
|
79
|
+
if: ${{ env.HAS_MACOS_CERTIFICATE == 'true' && env.HAS_MACOS_CERTIFICATE_PASSWORD == 'true' }}
|
80
|
+
run: |
|
81
|
+
# Remove temporary keychain
|
82
|
+
security delete-keychain temp.keychain || true
|
83
|
+
echo "🧹 Cleaned up code signing keychain"
|
84
|
+
|
85
|
+
- name: Find and verify DMG was created
|
86
|
+
id: find-dmg
|
87
|
+
run: |
|
88
|
+
ls -la gui_dist/
|
89
|
+
DMG_FILE=$(find gui_dist/ -name "*.dmg" -type f | head -1)
|
90
|
+
if [ -z "$DMG_FILE" ]; then
|
91
|
+
echo "❌ DMG file not found!"
|
92
|
+
echo "Contents of gui_dist/:"
|
93
|
+
ls -la gui_dist/ || echo "gui_dist/ directory not found"
|
94
|
+
exit 1
|
95
|
+
fi
|
96
|
+
echo "✅ DMG file created successfully: $DMG_FILE"
|
97
|
+
echo "dmg_path=$DMG_FILE" >> $GITHUB_OUTPUT
|
98
|
+
echo "dmg_name=$(basename $DMG_FILE)" >> $GITHUB_OUTPUT
|
99
|
+
|
100
|
+
- name: Get DMG info
|
101
|
+
id: dmg-info
|
102
|
+
run: |
|
103
|
+
DMG_SIZE=$(ls -lh "${{ steps.find-dmg.outputs.dmg_path }}" | awk '{print $5}')
|
104
|
+
echo "size=$DMG_SIZE" >> $GITHUB_OUTPUT
|
105
|
+
echo "📦 DMG Size: $DMG_SIZE"
|
106
|
+
echo "📦 DMG Name: ${{ steps.find-dmg.outputs.dmg_name }}"
|
107
|
+
|
108
|
+
- name: Upload DMG to Release
|
109
|
+
env:
|
110
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
111
|
+
run: |
|
112
|
+
gh release upload ${{ github.event.release.tag_name }} \
|
113
|
+
"${{ steps.find-dmg.outputs.dmg_path }}" \
|
114
|
+
--clobber \
|
115
|
+
--repo ${{ github.repository }}
|
116
|
+
|
@@ -0,0 +1,198 @@
|
|
1
|
+
name: Build and Attach Windows EXE to Release
|
2
|
+
|
3
|
+
on:
|
4
|
+
release:
|
5
|
+
types: [created]
|
6
|
+
|
7
|
+
permissions:
|
8
|
+
contents: write
|
9
|
+
|
10
|
+
jobs:
|
11
|
+
build-exe:
|
12
|
+
runs-on: windows-latest
|
13
|
+
env:
|
14
|
+
HAS_WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE != '' }}
|
15
|
+
HAS_WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD != '' }}
|
16
|
+
APP_DISPLAY_NAME: "TBR Deal Finder"
|
17
|
+
APP_FILENAME_BASE: "TBR-Deal-Finder"
|
18
|
+
|
19
|
+
steps:
|
20
|
+
- name: Checkout code
|
21
|
+
uses: actions/checkout@v4
|
22
|
+
|
23
|
+
- name: Set up Flutter
|
24
|
+
uses: subosito/flutter-action@v2
|
25
|
+
with:
|
26
|
+
flutter-version: '3.29.2'
|
27
|
+
channel: 'stable'
|
28
|
+
|
29
|
+
- name: Enable Windows Desktop
|
30
|
+
run: flutter config --enable-windows-desktop
|
31
|
+
|
32
|
+
- name: Set up Python
|
33
|
+
uses: actions/setup-python@v5
|
34
|
+
with:
|
35
|
+
python-version: '3.13'
|
36
|
+
|
37
|
+
- name: Install uv
|
38
|
+
uses: astral-sh/setup-uv@v5
|
39
|
+
|
40
|
+
- name: Install project dependencies
|
41
|
+
run: |
|
42
|
+
uv sync
|
43
|
+
|
44
|
+
- name: Import Code Signing Certificate (if available)
|
45
|
+
if: ${{ env.HAS_WINDOWS_CERTIFICATE == 'true' && env.HAS_WINDOWS_CERTIFICATE_PASSWORD == 'true' }}
|
46
|
+
run: |
|
47
|
+
# Decode and import certificate
|
48
|
+
$certBytes = [System.Convert]::FromBase64String("${{ secrets.WINDOWS_CERTIFICATE }}")
|
49
|
+
$certPath = "windows-cert.pfx"
|
50
|
+
[System.IO.File]::WriteAllBytes($certPath, $certBytes)
|
51
|
+
|
52
|
+
# Import certificate to certificate store
|
53
|
+
$securePassword = ConvertTo-SecureString -String "${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}" -Force -AsPlainText
|
54
|
+
Import-PfxCertificate -FilePath $certPath -CertStoreLocation Cert:\CurrentUser\My -Password $securePassword
|
55
|
+
|
56
|
+
Write-Output "✅ Code signing certificate imported successfully"
|
57
|
+
|
58
|
+
# Clean up certificate file
|
59
|
+
Remove-Item $certPath -Force
|
60
|
+
|
61
|
+
- name: Build Windows EXE
|
62
|
+
env:
|
63
|
+
PYTHONIOENCODING: utf-8
|
64
|
+
PYTHONLEGACYWINDOWSFSENCODING: utf-8
|
65
|
+
# Disable Rich progress bars to avoid Unicode issues
|
66
|
+
TERM: dumb
|
67
|
+
NO_COLOR: 1
|
68
|
+
run: |
|
69
|
+
# Set console to UTF-8 to handle Unicode characters
|
70
|
+
chcp 65001
|
71
|
+
|
72
|
+
# Clean any existing build cache to ensure fresh plugin compilation
|
73
|
+
Write-Output "🧹 Cleaning build cache..."
|
74
|
+
Remove-Item -Path "build" -Recurse -Force -ErrorAction SilentlyContinue
|
75
|
+
Remove-Item -Path "gui_dist" -Recurse -Force -ErrorAction SilentlyContinue
|
76
|
+
|
77
|
+
# Use flet to build Windows executable (Flutter environment now properly set up)
|
78
|
+
Write-Output "🪟 Building Windows executable..."
|
79
|
+
uv run flet build windows --output gui_dist/ --verbose
|
80
|
+
|
81
|
+
Write-Output "📁 Build output contents:"
|
82
|
+
Get-ChildItem -Path gui_dist -Recurse | Format-Table Name, Length, LastWriteTime
|
83
|
+
|
84
|
+
- name: Sign Windows EXE (if certificate available)
|
85
|
+
if: ${{ env.HAS_WINDOWS_CERTIFICATE == 'true' && env.HAS_WINDOWS_CERTIFICATE_PASSWORD == 'true' }}
|
86
|
+
uses: dlemstra/code-sign-action@v1
|
87
|
+
with:
|
88
|
+
certificate: '${{ secrets.WINDOWS_CERTIFICATE }}'
|
89
|
+
password: '${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}'
|
90
|
+
folder: 'gui_dist'
|
91
|
+
recursive: true
|
92
|
+
|
93
|
+
- name: Get App Info
|
94
|
+
id: app-info
|
95
|
+
run: |
|
96
|
+
# Find the EXE file
|
97
|
+
$exeFile = Get-ChildItem -Path "gui_dist" -Filter "*.exe" | Select-Object -First 1
|
98
|
+
if (-not $exeFile) {
|
99
|
+
Write-Output "❌ EXE file not found!"
|
100
|
+
exit 1
|
101
|
+
}
|
102
|
+
|
103
|
+
$exeName = [System.IO.Path]::GetFileNameWithoutExtension($exeFile.Name)
|
104
|
+
$appVersion = "${{ github.event.release.tag_name }}".TrimStart('v')
|
105
|
+
|
106
|
+
# Default to 1.0.0 if tag isn't semver
|
107
|
+
if (-not ($appVersion -match '^\d+\.\d+\.\d+')) {
|
108
|
+
$appVersion = "1.0.0"
|
109
|
+
}
|
110
|
+
|
111
|
+
Write-Output "exe_name=$($exeFile.Name)" >> $env:GITHUB_OUTPUT
|
112
|
+
Write-Output "exe_base_name=$exeName" >> $env:GITHUB_OUTPUT
|
113
|
+
Write-Output "app_version=$appVersion" >> $env:GITHUB_OUTPUT
|
114
|
+
|
115
|
+
- name: Install Inno Setup
|
116
|
+
run: |
|
117
|
+
# Download and install Inno Setup silently
|
118
|
+
Invoke-WebRequest -Uri "https://jrsoftware.org/download.php/is.exe" -OutFile "innosetup.exe"
|
119
|
+
Start-Process -FilePath ".\innosetup.exe" -ArgumentList "/VERYSILENT", "/SUPPRESSMSGBOXES", "/NORESTART", "/SP-" -Wait
|
120
|
+
|
121
|
+
# Add to PATH for current session
|
122
|
+
$env:PATH += ";C:\Program Files (x86)\Inno Setup 6"
|
123
|
+
|
124
|
+
Write-Output "✅ Inno Setup installed successfully"
|
125
|
+
|
126
|
+
- name: Create InnoSetup Script
|
127
|
+
run: |
|
128
|
+
@"
|
129
|
+
[Setup]
|
130
|
+
AppId={{$(New-Guid)}}
|
131
|
+
AppName=${{ env.APP_DISPLAY_NAME }}
|
132
|
+
AppVersion=${{ steps.app-info.outputs.app_version }}
|
133
|
+
AppPublisher=WillNye
|
134
|
+
AppPublisherURL=https://github.com/WillNye
|
135
|
+
AppSupportURL=https://github.com/WillNye/${{ github.event.repository.name }}/issues
|
136
|
+
AppUpdatesURL=https://github.com/WillNye/${{ github.event.repository.name }}/releases
|
137
|
+
DefaultDirName={autopf}\${{ env.APP_DISPLAY_NAME }}
|
138
|
+
DefaultGroupName=${{ env.APP_DISPLAY_NAME }}
|
139
|
+
AllowNoIcons=yes
|
140
|
+
OutputDir=.
|
141
|
+
OutputBaseFilename=${{ env.APP_FILENAME_BASE }}-${{ steps.app-info.outputs.app_version }}-Windows-Setup
|
142
|
+
SetupIconFile=assets\icon.ico
|
143
|
+
Compression=lzma2/normal
|
144
|
+
SolidCompression=no
|
145
|
+
WizardStyle=modern
|
146
|
+
PrivilegesRequired=lowest
|
147
|
+
PrivilegesRequiredOverridesAllowed=dialog
|
148
|
+
|
149
|
+
[Languages]
|
150
|
+
Name: "english"; MessagesFile: "compiler:Default.isl"
|
151
|
+
|
152
|
+
[Tasks]
|
153
|
+
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
154
|
+
|
155
|
+
[Files]
|
156
|
+
; Exclude EXE from compression to preserve icon quality
|
157
|
+
Source: "gui_dist\${{ steps.app-info.outputs.exe_name }}"; DestDir: "{app}"; Flags: ignoreversion nocompression
|
158
|
+
; Compress everything else
|
159
|
+
Source: "gui_dist\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "${{ steps.app-info.outputs.exe_name }}"
|
160
|
+
|
161
|
+
[Icons]
|
162
|
+
Name: "{group}\${{ env.APP_DISPLAY_NAME }}"; Filename: "{app}\${{ steps.app-info.outputs.exe_name }}"
|
163
|
+
Name: "{group}\{cm:UninstallProgram,${{ env.APP_DISPLAY_NAME }}}"; Filename: "{uninstallexe}"
|
164
|
+
Name: "{autodesktop}\${{ env.APP_DISPLAY_NAME }}"; Filename: "{app}\${{ steps.app-info.outputs.exe_name }}"; Tasks: desktopicon
|
165
|
+
|
166
|
+
[Run]
|
167
|
+
Filename: "{app}\${{ steps.app-info.outputs.exe_name }}"; Description: "{cm:LaunchProgram,${{ env.APP_DISPLAY_NAME }}}"; Flags: nowait postinstall skipifsilent
|
168
|
+
"@ | Out-File -FilePath "setup.iss" -Encoding UTF8
|
169
|
+
- name: Build Setup with InnoSetup
|
170
|
+
run: |
|
171
|
+
# Compile the installer
|
172
|
+
& "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" setup.iss
|
173
|
+
|
174
|
+
if ($LASTEXITCODE -ne 0) {
|
175
|
+
Write-Output "❌ InnoSetup compilation failed with exit code: $LASTEXITCODE"
|
176
|
+
exit 1
|
177
|
+
}
|
178
|
+
|
179
|
+
Write-Output "✅ Installer created successfully"
|
180
|
+
|
181
|
+
- name: Sign Installer (if certificate available)
|
182
|
+
if: ${{ env.HAS_WINDOWS_CERTIFICATE == 'true' && env.HAS_WINDOWS_CERTIFICATE_PASSWORD == 'true' }}
|
183
|
+
uses: dlemstra/code-sign-action@v1
|
184
|
+
with:
|
185
|
+
certificate: '${{ secrets.WINDOWS_CERTIFICATE }}'
|
186
|
+
password: '${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}'
|
187
|
+
files: '${{ env.APP_FILENAME_BASE }}-${{ steps.app-info.outputs.app_version }}-Windows-Setup.exe'
|
188
|
+
|
189
|
+
- name: Upload Installer to Release
|
190
|
+
env:
|
191
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
192
|
+
run: |
|
193
|
+
$installerFile = "${{ env.APP_FILENAME_BASE }}-${{ steps.app-info.outputs.app_version }}-Windows-Setup.exe"
|
194
|
+
|
195
|
+
gh release upload ${{ github.event.release.tag_name }} `
|
196
|
+
$installerFile `
|
197
|
+
--clobber `
|
198
|
+
--repo ${{ github.repository }}
|
@@ -0,0 +1,111 @@
|
|
1
|
+
|
2
|
+
# Change Log
|
3
|
+
|
4
|
+
---
|
5
|
+
|
6
|
+
## 0.3.2 (September 8, 2025)
|
7
|
+
|
8
|
+
Notes:
|
9
|
+
* Disable nav bar buttons when performing certain operations
|
10
|
+
* Added config check to CLI location when running the desktop app for backwards compatibility
|
11
|
+
* Improved performance when retrieving "latest deals"
|
12
|
+
|
13
|
+
BUG FIXES:
|
14
|
+
* Fixed issue with scroll bar in the "all deals" page
|
15
|
+
|
16
|
+
---
|
17
|
+
|
18
|
+
## 0.3.1 (September 5, 2025)
|
19
|
+
|
20
|
+
Notes:
|
21
|
+
* Added a GUI to serve as a desktop app
|
22
|
+
* Added retry behavior to Kindle and Libro on get_book call
|
23
|
+
* Saving all pricing info to be used for historical pricing details
|
24
|
+
* Added version checking to the CLI
|
25
|
+
|
26
|
+
BUG FIXES:
|
27
|
+
* Improvements to grouping titles in TBR
|
28
|
+
* Fixed issue where known books were being marked as unknown if not found on a run
|
29
|
+
* Fixed bug where books not found were sometimes not added to Unknown books
|
30
|
+
* Fixed regression on Windows devices when adding Kindle support
|
31
|
+
|
32
|
+
---
|
33
|
+
|
34
|
+
## 0.2.1 (August 25, 2025)
|
35
|
+
|
36
|
+
Notes:
|
37
|
+
* Added Kindle Library support
|
38
|
+
* Wishlist support is looking unlikely
|
39
|
+
* Running into auth issues on the only viable endpoint https://www.amazon.com/kindle-reader-api
|
40
|
+
* No longer attempting to retrieve details on books not previously found on every run
|
41
|
+
* Full check is now performed weekly or when a change has been made to the user config
|
42
|
+
|
43
|
+
BUG FIXES:
|
44
|
+
* Failed Libro login no longer causing crash
|
45
|
+
|
46
|
+
---
|
47
|
+
|
48
|
+
## 0.2.0 (August 15, 2025)
|
49
|
+
|
50
|
+
Notes:
|
51
|
+
* Added foundational Kindle support
|
52
|
+
* Library support is undecided right now
|
53
|
+
* Unable to find the endpoint
|
54
|
+
* Wishlist support is undecided right now
|
55
|
+
* Unable to find the endpoint
|
56
|
+
* Improvements to title matching for Audible & Chirp
|
57
|
+
* Improved request performance for Chirp & Libro
|
58
|
+
|
59
|
+
BUG FIXES:
|
60
|
+
* Fixed breaking import on Windows systems
|
61
|
+
* Fixed displayed discount percent
|
62
|
+
|
63
|
+
---
|
64
|
+
|
65
|
+
## 0.1.8 (August 13, 2025)
|
66
|
+
|
67
|
+
Notes:
|
68
|
+
* Improved performance for tracking on libro
|
69
|
+
* Preparing EBook support
|
70
|
+
|
71
|
+
BUG FIXES:
|
72
|
+
* Fixed initial login issue in libro.fm
|
73
|
+
|
74
|
+
---
|
75
|
+
|
76
|
+
## 0.1.7 (July 31, 2025)
|
77
|
+
|
78
|
+
Notes:
|
79
|
+
* tbr-deal-finder no longer shows deals on books you own in the same format.
|
80
|
+
* Example: You own Dune on Audible so it won't show on Audible, Libro, or Chirp. It will show on Kindle (you don't own the ebook)
|
81
|
+
* Improvements when attempting to match authors
|
82
|
+
* Chirp
|
83
|
+
* Libro.FM
|
84
|
+
* Users no longer need to provide an export and can instead just track deals on their wishlist
|
85
|
+
|
86
|
+
BUG FIXES:
|
87
|
+
* Fixed wishlist pagination in libro.fm
|
88
|
+
* Fixed issue forcing user to go through setup twice when running the setup command
|
89
|
+
|
90
|
+
---
|
91
|
+
|
92
|
+
## 0.1.6 (July 30, 2025)
|
93
|
+
|
94
|
+
Notes:
|
95
|
+
* tbr-deal-finder now also tracks deals on the books in your wishlist. Works for all retailers.
|
96
|
+
|
97
|
+
BUG FIXES:
|
98
|
+
* Fixed issue where no deals would display if libro is the only tracked audiobook retailer.
|
99
|
+
* Fixed retailer cli setup forcing a user to select at least two audiobook retailers.
|
100
|
+
|
101
|
+
---
|
102
|
+
|
103
|
+
## 0.1.5 (July 30, 2025)
|
104
|
+
|
105
|
+
Notes:
|
106
|
+
* Added formatting to select messages to make the messages purpose clearer.
|
107
|
+
|
108
|
+
BUG FIXES:
|
109
|
+
* Fixed issue getting books from libro and chirp too aggressively
|
110
|
+
* User must now track deals for at least one retailer
|
111
|
+
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# TBR Deal Finder Design
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
TBR Deal Finder is a CLI application that helps users track price drops and find deals on books in their To-Be-Read (TBR) list across various digital book retailers. The application is built with a modular architecture that separates data ingestion, processing, and output concerns.
|
5
|
+
|
6
|
+
## Terms
|
7
|
+
### Digital Book
|
8
|
+
A book in electronic format, accessible via digital devices. Includes:
|
9
|
+
- Audiobooks
|
10
|
+
- E-books
|
11
|
+
|
12
|
+
### Digital Book Retailer
|
13
|
+
Platforms that sell or distribute digital books, such as:
|
14
|
+
- Libro.fm (audiobooks)
|
15
|
+
- Audible (audiobooks)
|
16
|
+
- Kindle (e-books)
|
17
|
+
- Chirp (discounted audiobooks)
|
18
|
+
|
19
|
+
### Library Export
|
20
|
+
A data file containing a user's reading list and status. Sources include:
|
21
|
+
- **Automated Exports**:
|
22
|
+
- The StoryGraph
|
23
|
+
- Goodreads
|
24
|
+
- **Manual CSVs**: Custom spreadsheets following the required format
|
25
|
+
|
26
|
+
### TBR (To Be Read)
|
27
|
+
A user's personal reading list of books they plan to read in the future.
|
28
|
+
|
29
|
+
## Core Components
|
30
|
+
|
31
|
+
### 1. Data Ingestion Layer
|
32
|
+
- **Library Exports**: Handles importing book data from multiple sources:
|
33
|
+
- The StoryGraph exports
|
34
|
+
- Goodreads exports
|
35
|
+
- Custom CSV files
|
36
|
+
- **Retailer APIs**: Interfaces with various digital book retailers to fetch current pricing and availability
|
37
|
+
|
38
|
+
### 2. Core Data Model
|
39
|
+
|
40
|
+
#### `Book` Class
|
41
|
+
The central data structure that represents a book across the application:
|
42
|
+
- **Purpose**: Serves as a consistent contract between different components
|
43
|
+
- **Key Attributes**:
|
44
|
+
- Title and author information
|
45
|
+
- Format (Audiobook, Ebook, etc.)
|
46
|
+
- Pricing information (list price, current price)
|
47
|
+
- Retailer-specific metadata
|
48
|
+
- Timestamp for deal tracking
|
49
|
+
|
50
|
+
### 3. Retailer Interface
|
51
|
+
|
52
|
+
#### `Retailer` Base Class
|
53
|
+
Abstract base class that defines the interface for all retailer implementations:
|
54
|
+
- **Core Methods**:
|
55
|
+
- `get_book()`: Fetches book details from the retailer
|
56
|
+
- `get_wishlist()`: Retrieves the user's wishlist
|
57
|
+
- `get_library()`: Gets the user's purchased/owned books
|
58
|
+
- **Current Implementations**:
|
59
|
+
- Audible
|
60
|
+
- Libro.fm
|
61
|
+
- Chirp
|
62
|
+
- Kindle (planned)
|
63
|
+
|
64
|
+
### 4. Processing Pipeline
|
65
|
+
1. **Data Collection**:
|
66
|
+
- Load TBR lists from configured sources
|
67
|
+
- Fetch owned books from retailers
|
68
|
+
- Retrieve current deals and pricing
|
69
|
+
|
70
|
+
2. **Matching Logic**:
|
71
|
+
- Matches TBR books with retailer inventory
|
72
|
+
- Filters out owned books
|
73
|
+
- Identifies price drops and deals
|
74
|
+
|
75
|
+
3. **Output Generation**:
|
76
|
+
- Formats results for CLI display
|
77
|
+
- Highlights best deals
|
78
|
+
- Provides purchase links
|
79
|
+
|
80
|
+
## Data Flow
|
81
|
+
|
82
|
+
```mermaid
|
83
|
+
graph TD
|
84
|
+
A[Library Exports] -->|CSV/JSON| B[Book Objects]
|
85
|
+
C[Retailer APIs] -->|API Calls| B
|
86
|
+
B --> D[Deal Processing]
|
87
|
+
D --> E[CLI Output]
|
88
|
+
F[User Configuration] -->|Settings| C
|
89
|
+
F -->|Preferences| D
|
90
|
+
```
|
91
|
+
|
92
|
+
## Key Design Decisions
|
93
|
+
|
94
|
+
### 1. Extensibility
|
95
|
+
- Retailer implementations are pluggable through the `Retailer` base class
|
96
|
+
- Support for multiple export formats and retailer APIs
|
97
|
+
|
98
|
+
### 2. Performance
|
99
|
+
- Asynchronous I/O for API calls
|
100
|
+
- Caching of retailer responses
|
101
|
+
- Efficient data structures for book matching
|
102
|
+
|
103
|
+
### 3. User Experience
|
104
|
+
- Clear, concise command-line interface
|
105
|
+
- Configurable output formats
|
106
|
+
- Progress indicators for long-running operations
|
107
|
+
|
108
|
+
## Future Considerations
|
109
|
+
- Support for more retailer APIs
|
110
|
+
- Email notifications for price drops
|
111
|
+
- Web interface for easier configuration
|
112
|
+
- Integration with library lending services
|
113
|
+
- Price history tracking
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# TBR Deal Finder Build System
|
2
|
+
.PHONY: help build-mac build-windows build-windows-docker build-linux build-all clean clean-all test-mac status create-cert
|
3
|
+
|
4
|
+
# Default target
|
5
|
+
help:
|
6
|
+
@echo "TBR Deal Finder Build Commands:"
|
7
|
+
@echo ""
|
8
|
+
@echo "Setup:"
|
9
|
+
@echo " make create-cert Create self-signed certificate (one-time setup) 🔐"
|
10
|
+
@echo ""
|
11
|
+
@echo "Building:"
|
12
|
+
@echo " make build-mac Build self-signed macOS DMG ⭐"
|
13
|
+
@echo " make build-windows Build Windows EXE (GitHub Actions) ⭐"
|
14
|
+
@echo " make build-linux Build Linux executable"
|
15
|
+
@echo " make build-all Build for current platform"
|
16
|
+
@echo ""
|
17
|
+
@echo "Testing:"
|
18
|
+
@echo " make test-mac Test macOS DMG"
|
19
|
+
@echo ""
|
20
|
+
@echo "Utilities:"
|
21
|
+
@echo " make clean Clean build artifacts (keeps .spec)"
|
22
|
+
@echo " make clean-all Clean everything including .spec"
|
23
|
+
@echo ""
|
24
|
+
@echo "Current platform: $(shell uname -s)"
|
25
|
+
|
26
|
+
# Variables
|
27
|
+
PROJECT_NAME := TBR Deal Finder
|
28
|
+
DIST_DIR := gui_dist
|
29
|
+
BUILD_SCRIPT := scripts/packaging/build_cross_platform.py
|
30
|
+
|
31
|
+
# Build self-signed macOS DMG (recommended)
|
32
|
+
build-mac:
|
33
|
+
@echo "🍎 Building app"
|
34
|
+
uv run flet build macos --output ${DIST_DIR}/app/
|
35
|
+
@echo ""
|
36
|
+
@echo "📦 Creating self-signed macOS DMG for app"
|
37
|
+
bash scripts/packaging/create_dmg.sh
|
38
|
+
@echo "✅ Self-signed macOS DMG built successfully!"
|
39
|
+
|
40
|
+
# Build Windows EXE (requires Windows or GitHub Actions)
|
41
|
+
build-windows:
|
42
|
+
@echo "🪟 Building Windows EXE..."
|
43
|
+
uv run flet build windows --output ${DIST_DIR}/ --verbose
|
44
|
+
|
45
|
+
# Test macOS DMG
|
46
|
+
test-mac:
|
47
|
+
@echo "🧪 Testing macOS DMG..."
|
48
|
+
@if [ ! -f "$(DIST_DIR)/TBRDealFinder.dmg" ]; then \
|
49
|
+
echo "❌ No DMG found. Run 'make build-mac' first."; \
|
50
|
+
exit 1; \
|
51
|
+
fi
|
52
|
+
@echo "📂 DMG file info:" && \
|
53
|
+
ls -lh $(DIST_DIR)/TBRDealFinder.dmg && \
|
54
|
+
echo "🔍 Testing DMG mount..." && \
|
55
|
+
hdiutil attach $(DIST_DIR)/TBRDealFinder.dmg -mountpoint /tmp/tbr_test -nobrowse && \
|
56
|
+
echo "✅ DMG mounts successfully" && \
|
57
|
+
ls -la /tmp/tbr_test/ && \
|
58
|
+
hdiutil detach /tmp/tbr_test 2>/dev/null || \
|
59
|
+
echo "❌ DMG mount failed"
|
60
|
+
|
61
|
+
# Build Linux executable (works on Linux)
|
62
|
+
build-linux:
|
63
|
+
@echo "🐧 Building Linux executable..."
|
64
|
+
@if [ "$(shell uname -s)" != "Linux" ]; then \
|
65
|
+
echo "⚠️ Linux builds require Linux OS"; \
|
66
|
+
echo " Run this on Linux or use GitHub Actions for cross-platform builds"; \
|
67
|
+
exit 1; \
|
68
|
+
fi
|
69
|
+
uv run python $(BUILD_SCRIPT)
|
70
|
+
@echo ""
|
71
|
+
@echo "✅ Linux executable built successfully!"
|
72
|
+
@echo "📦 Output: $(DIST_DIR)/TBRDealFinder"
|
73
|
+
@ls -lh $(DIST_DIR)/TBRDealFinder 2>/dev/null || true
|
74
|
+
|
75
|
+
# Build for current platform
|
76
|
+
build-all:
|
77
|
+
@echo "🌍 Building for current platform..."
|
78
|
+
@case "$(shell uname -s)" in \
|
79
|
+
Darwin) \
|
80
|
+
echo "On macOS - building self-signed DMG"; \
|
81
|
+
$(MAKE) build-mac; \
|
82
|
+
;; \
|
83
|
+
Linux) \
|
84
|
+
echo "On Linux - building executable"; \
|
85
|
+
$(MAKE) build-linux; \
|
86
|
+
;; \
|
87
|
+
MINGW*|MSYS*|CYGWIN*) \
|
88
|
+
echo "On Windows - building Windows EXE"; \
|
89
|
+
$(MAKE) build-windows; \
|
90
|
+
;; \
|
91
|
+
*) \
|
92
|
+
if [ "$(OS)" = "Windows_NT" ]; then \
|
93
|
+
echo "On Windows - building Windows EXE"; \
|
94
|
+
$(MAKE) build-windows; \
|
95
|
+
else \
|
96
|
+
echo "❌ Unsupported platform: $(shell uname -s)"; \
|
97
|
+
echo " Supported: macOS (with Docker), Windows, Linux"; \
|
98
|
+
exit 1; \
|
99
|
+
fi; \
|
100
|
+
;; \
|
101
|
+
esac
|
102
|
+
|
103
|
+
# Clean build artifacts (preserves .spec file for version control)
|
104
|
+
clean:
|
105
|
+
@echo "🧹 Cleaning build artifacts..."
|
106
|
+
rm -rf $(DIST_DIR)/
|
107
|
+
rm -rf build/
|
108
|
+
@echo "✅ Clean complete (kept .spec file)"
|
109
|
+
|
110
|
+
# Clean everything including .spec file
|
111
|
+
clean-all:
|
112
|
+
@echo "🧹 Cleaning everything..."
|
113
|
+
rm -rf $(DIST_DIR)/
|
114
|
+
rm -rf build/
|
115
|
+
rm -rf dist/
|
116
|
+
rm -f *.spec
|
117
|
+
@echo "✅ Complete clean finished"
|
118
|
+
|
119
|
+
# Create self-signed certificate for consistent code signing
|
120
|
+
create-cert:
|
121
|
+
@echo "🔐 Creating self-signed certificate for consistent code signing..."
|
122
|
+
@echo ""
|
123
|
+
@bash scripts/create-self-signed-cert.sh
|
124
|
+
|
125
|
+
# Show build status
|
126
|
+
status:
|
127
|
+
@echo "📊 Build Status:"
|
128
|
+
@echo ""
|
129
|
+
@echo "Platform: $(shell uname -s)"
|
130
|
+
@echo "Build directory: $(DIST_DIR)/"
|
131
|
+
@echo ""
|
132
|
+
@echo "Built artifacts:"
|
133
|
+
@if [ -d "$(DIST_DIR)" ]; then \
|
134
|
+
ls -la $(DIST_DIR)/ 2>/dev/null || echo " None found"; \
|
135
|
+
else \
|
136
|
+
echo " Build directory doesn't exist"; \
|
137
|
+
fi
|
138
|
+
@echo ""
|
139
|
+
@echo "Dependencies:"
|
140
|
+
@uv tree --depth 1 | grep -E "(flet|pyinstaller)" || echo " Not installed"
|
141
|
+
@echo ""
|
142
|
+
@echo "Code signing certificates:"
|
143
|
+
@security find-identity -v -p codesigning 2>/dev/null | grep -E "(TBR Deal Finder|Developer ID)" || echo " None found (will use ad-hoc signing)"
|