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.
Files changed (64) hide show
  1. tbr_deal_finder-0.3.2/.github/workflows/build-release-dmg.yml +116 -0
  2. tbr_deal_finder-0.3.2/.github/workflows/build-release-exe.yml +198 -0
  3. {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/.gitignore +3 -0
  4. tbr_deal_finder-0.3.2/CHANGELOG.md +111 -0
  5. tbr_deal_finder-0.3.2/DESIGN.md +113 -0
  6. tbr_deal_finder-0.3.2/Makefile +143 -0
  7. tbr_deal_finder-0.3.2/PKG-INFO +104 -0
  8. tbr_deal_finder-0.3.2/README.md +82 -0
  9. tbr_deal_finder-0.3.2/assets/icon.ico +0 -0
  10. tbr_deal_finder-0.3.2/assets/icon.png +0 -0
  11. tbr_deal_finder-0.3.2/docs/desktop-app.md +133 -0
  12. tbr_deal_finder-0.3.2/docs/development.md +198 -0
  13. tbr_deal_finder-0.3.2/docs/python-cli.md +413 -0
  14. {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/pyproject.toml +14 -2
  15. tbr_deal_finder-0.3.2/scripts/packaging/create_dmg.sh +96 -0
  16. tbr_deal_finder-0.3.2/tbr_deal_finder/__init__.py +5 -0
  17. tbr_deal_finder-0.3.2/tbr_deal_finder/__main__.py +7 -0
  18. tbr_deal_finder-0.3.2/tbr_deal_finder/book.py +226 -0
  19. {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/tbr_deal_finder/cli.py +48 -55
  20. {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/tbr_deal_finder/config.py +23 -5
  21. tbr_deal_finder-0.3.2/tbr_deal_finder/desktop_updater.py +147 -0
  22. tbr_deal_finder-0.3.2/tbr_deal_finder/gui/__init__.py +0 -0
  23. tbr_deal_finder-0.3.2/tbr_deal_finder/gui/main.py +754 -0
  24. tbr_deal_finder-0.3.2/tbr_deal_finder/gui/pages/__init__.py +1 -0
  25. tbr_deal_finder-0.3.2/tbr_deal_finder/gui/pages/all_books.py +100 -0
  26. tbr_deal_finder-0.3.2/tbr_deal_finder/gui/pages/all_deals.py +63 -0
  27. tbr_deal_finder-0.3.2/tbr_deal_finder/gui/pages/base_book_page.py +290 -0
  28. tbr_deal_finder-0.3.2/tbr_deal_finder/gui/pages/book_details.py +604 -0
  29. tbr_deal_finder-0.3.2/tbr_deal_finder/gui/pages/latest_deals.py +376 -0
  30. tbr_deal_finder-0.3.2/tbr_deal_finder/gui/pages/settings.py +390 -0
  31. {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/tbr_deal_finder/migrations.py +42 -0
  32. tbr_deal_finder-0.3.2/tbr_deal_finder/owned_books.py +18 -0
  33. {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/tbr_deal_finder/queries/get_active_deals.sql +1 -1
  34. {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/tbr_deal_finder/queries/get_deals_found_at.sql +1 -1
  35. tbr_deal_finder-0.3.2/tbr_deal_finder/queries/latest_unknown_book_sync.sql +5 -0
  36. {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/tbr_deal_finder/retailer/__init__.py +2 -0
  37. tbr_deal_finder-0.3.2/tbr_deal_finder/retailer/amazon.py +136 -0
  38. tbr_deal_finder-0.3.2/tbr_deal_finder/retailer/amazon_custom_auth.py +79 -0
  39. tbr_deal_finder-0.3.2/tbr_deal_finder/retailer/audible.py +123 -0
  40. tbr_deal_finder-0.3.2/tbr_deal_finder/retailer/chirp.py +264 -0
  41. tbr_deal_finder-0.3.2/tbr_deal_finder/retailer/kindle.py +165 -0
  42. tbr_deal_finder-0.3.2/tbr_deal_finder/retailer/librofm.py +233 -0
  43. tbr_deal_finder-0.3.2/tbr_deal_finder/retailer/models.py +120 -0
  44. {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/tbr_deal_finder/retailer_deal.py +104 -60
  45. tbr_deal_finder-0.3.2/tbr_deal_finder/tracked_books.py +407 -0
  46. tbr_deal_finder-0.3.2/tbr_deal_finder/utils.py +128 -0
  47. tbr_deal_finder-0.3.2/tbr_deal_finder/version_check.py +40 -0
  48. tbr_deal_finder-0.3.2/tbr_deal_finder.py +7 -0
  49. {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/uv.lock +819 -1
  50. tbr_deal_finder-0.1.5/CHANGELOG.md +0 -12
  51. tbr_deal_finder-0.1.5/PKG-INFO +0 -167
  52. tbr_deal_finder-0.1.5/README.md +0 -150
  53. tbr_deal_finder-0.1.5/tbr_deal_finder/__init__.py +0 -9
  54. tbr_deal_finder-0.1.5/tbr_deal_finder/book.py +0 -116
  55. tbr_deal_finder-0.1.5/tbr_deal_finder/library_exports.py +0 -155
  56. tbr_deal_finder-0.1.5/tbr_deal_finder/retailer/audible.py +0 -146
  57. tbr_deal_finder-0.1.5/tbr_deal_finder/retailer/chirp.py +0 -80
  58. tbr_deal_finder-0.1.5/tbr_deal_finder/retailer/librofm.py +0 -140
  59. tbr_deal_finder-0.1.5/tbr_deal_finder/retailer/models.py +0 -37
  60. tbr_deal_finder-0.1.5/tbr_deal_finder/utils.py +0 -57
  61. {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/.github/workflows/publish-to-pypi.yaml +0 -0
  62. {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/.python-version +0 -0
  63. {tbr_deal_finder-0.1.5 → tbr_deal_finder-0.3.2}/LICENSE +0 -0
  64. {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 }}
@@ -206,3 +206,6 @@ cython_debug/
206
206
  marimo/_static/
207
207
  marimo/_lsp/
208
208
  __marimo__/
209
+
210
+ gui_dist/
211
+ scratch/
@@ -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)"