scrobbles 0.1.0__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.
@@ -0,0 +1,32 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ build:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - uses: astral-sh/setup-uv@v6
13
+ - run: uv version "${GITHUB_REF_NAME#v}"
14
+ - run: uv build
15
+ - uses: actions/upload-artifact@v4
16
+ with:
17
+ name: dist
18
+ path: dist/
19
+
20
+ publish:
21
+ needs: build
22
+ runs-on: ubuntu-latest
23
+ environment: pypi
24
+ permissions:
25
+ id-token: write
26
+ steps:
27
+ - uses: astral-sh/setup-uv@v6
28
+ - uses: actions/download-artifact@v4
29
+ with:
30
+ name: dist
31
+ path: dist/
32
+ - run: uv publish dist/*
@@ -0,0 +1,213 @@
1
+ # Claude Code
2
+ .claude/
3
+
4
+ # User data
5
+ data/
6
+
7
+ # Byte-compiled / optimized / DLL files
8
+ __pycache__/
9
+ *.py[codz]
10
+ *$py.class
11
+
12
+ # C extensions
13
+ *.so
14
+
15
+ # Distribution / packaging
16
+ .Python
17
+ build/
18
+ develop-eggs/
19
+ dist/
20
+ downloads/
21
+ eggs/
22
+ .eggs/
23
+ lib/
24
+ lib64/
25
+ parts/
26
+ sdist/
27
+ var/
28
+ wheels/
29
+ share/python-wheels/
30
+ *.egg-info/
31
+ .installed.cfg
32
+ *.egg
33
+ MANIFEST
34
+
35
+ # PyInstaller
36
+ # Usually these files are written by a python script from a template
37
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
38
+ *.manifest
39
+ *.spec
40
+
41
+ # Installer logs
42
+ pip-log.txt
43
+ pip-delete-this-directory.txt
44
+
45
+ # Unit test / coverage reports
46
+ htmlcov/
47
+ .tox/
48
+ .nox/
49
+ .coverage
50
+ .coverage.*
51
+ .cache
52
+ nosetests.xml
53
+ coverage.xml
54
+ *.cover
55
+ *.py.cover
56
+ .hypothesis/
57
+ .pytest_cache/
58
+ cover/
59
+
60
+ # Translations
61
+ *.mo
62
+ *.pot
63
+
64
+ # Django stuff:
65
+ *.log
66
+ local_settings.py
67
+ db.sqlite3
68
+ db.sqlite3-journal
69
+
70
+ # Flask stuff:
71
+ instance/
72
+ .webassets-cache
73
+
74
+ # Scrapy stuff:
75
+ .scrapy
76
+
77
+ # Sphinx documentation
78
+ docs/_build/
79
+
80
+ # PyBuilder
81
+ .pybuilder/
82
+ target/
83
+
84
+ # Jupyter Notebook
85
+ .ipynb_checkpoints
86
+
87
+ # IPython
88
+ profile_default/
89
+ ipython_config.py
90
+
91
+ # pyenv
92
+ # For a library or package, you might want to ignore these files since the code is
93
+ # intended to run in multiple environments; otherwise, check them in:
94
+ # .python-version
95
+
96
+ # pipenv
97
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
98
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
99
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
100
+ # install all needed dependencies.
101
+ #Pipfile.lock
102
+
103
+ # UV
104
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ #uv.lock
108
+
109
+ # poetry
110
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
111
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
112
+ # commonly ignored for libraries.
113
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
114
+ #poetry.lock
115
+ #poetry.toml
116
+
117
+ # pdm
118
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
119
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
120
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
121
+ #pdm.lock
122
+ #pdm.toml
123
+ .pdm-python
124
+ .pdm-build/
125
+
126
+ # pixi
127
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
128
+ #pixi.lock
129
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
130
+ # in the .venv directory. It is recommended not to include this directory in version control.
131
+ .pixi
132
+
133
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
134
+ __pypackages__/
135
+
136
+ # Celery stuff
137
+ celerybeat-schedule
138
+ celerybeat.pid
139
+
140
+ # SageMath parsed files
141
+ *.sage.py
142
+
143
+ # Environments
144
+ .env
145
+ .envrc
146
+ .venv
147
+ env/
148
+ venv/
149
+ ENV/
150
+ env.bak/
151
+ venv.bak/
152
+
153
+ # Spyder project settings
154
+ .spyderproject
155
+ .spyproject
156
+
157
+ # Rope project settings
158
+ .ropeproject
159
+
160
+ # mkdocs documentation
161
+ /site
162
+
163
+ # mypy
164
+ .mypy_cache/
165
+ .dmypy.json
166
+ dmypy.json
167
+
168
+ # Pyre type checker
169
+ .pyre/
170
+
171
+ # pytype static type analyzer
172
+ .pytype/
173
+
174
+ # Cython debug symbols
175
+ cython_debug/
176
+
177
+ # PyCharm
178
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
179
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
180
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
181
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
182
+ #.idea/
183
+
184
+ # Abstra
185
+ # Abstra is an AI-powered process automation framework.
186
+ # Ignore directories containing user credentials, local state, and settings.
187
+ # Learn more at https://abstra.io/docs
188
+ .abstra/
189
+
190
+ # Visual Studio Code
191
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
192
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
193
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
194
+ # you could uncomment the following to ignore the entire vscode folder
195
+ # .vscode/
196
+
197
+ # Ruff stuff:
198
+ .ruff_cache/
199
+
200
+ # PyPI configuration file
201
+ .pypirc
202
+
203
+ # Cursor
204
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
205
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
206
+ # refer to https://docs.cursor.com/context/ignore-files
207
+ .cursorignore
208
+ .cursorindexingignore
209
+
210
+ # Marimo
211
+ marimo/_static/
212
+ marimo/_lsp/
213
+ __marimo__/
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sam Foreman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,175 @@
1
+ Metadata-Version: 2.4
2
+ Name: scrobbles
3
+ Version: 0.1.0
4
+ Summary: Fetch, export, and visualize Last.fm listening data — plus create Spotify playlists from your top albums
5
+ Project-URL: Homepage, https://github.com/saforem2/scrobbles
6
+ Project-URL: Repository, https://github.com/saforem2/scrobbles
7
+ Project-URL: Issues, https://github.com/saforem2/scrobbles/issues
8
+ Author-email: Sam Foreman <saforem2@gmail.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: audioscrobbler,last.fm,lastfm,music,scrobble,spotify
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Multimedia :: Sound/Audio
17
+ Requires-Python: >=3.12
18
+ Requires-Dist: pylast>=7.0.2
19
+ Requires-Dist: spotipy>=2.26.0
20
+ Description-Content-Type: text/markdown
21
+
22
+ # scrobbles
23
+
24
+ Fetch, export, and visualize [Last.fm](https://www.last.fm/) listening data — plus create Spotify playlists from your top albums.
25
+
26
+ ## Setup
27
+
28
+ ### Prerequisites
29
+
30
+ - Python 3.12+
31
+ - [uv](https://docs.astral.sh/uv/) (recommended) or pip
32
+
33
+ ### Install
34
+
35
+ ```bash
36
+ pip install scrobbles
37
+ # or
38
+ uv add scrobbles
39
+ ```
40
+
41
+ #### From source
42
+
43
+ ```bash
44
+ git clone https://github.com/saforem2/scrobbles.git
45
+ cd lastfm
46
+ uv sync
47
+ ```
48
+
49
+ ### Environment Variables
50
+
51
+ #### Last.fm
52
+
53
+ Get API credentials at <https://www.last.fm/api/account>.
54
+
55
+ ```bash
56
+ export LASTFM_API_KEY="..."
57
+ export LASTFM_API_SECRET="..."
58
+ export LASTFM_USERNAME="..."
59
+ export LASTFM_PASSWORD="..."
60
+ ```
61
+
62
+ #### Spotify (optional — only needed for playlist creation)
63
+
64
+ 1. Create an app at <https://developer.spotify.com/dashboard>
65
+ 2. Add `http://127.0.0.1:8888/callback` as a Redirect URI in your app settings
66
+ 3. Export your credentials:
67
+
68
+ ```bash
69
+ export SPOTIFY_CLIENT_ID="..." # or SPOTIPY_CLIENT_ID
70
+ export SPOTIFY_CLIENT_SECRET="..." # or SPOTIPY_CLIENT_SECRET
71
+ export SPOTIPY_REDIRECT_URI="http://127.0.0.1:8888/callback"
72
+ ```
73
+
74
+ Either `SPOTIFY_*` or `SPOTIPY_*` naming works — the library maps them automatically.
75
+
76
+ ## Usage
77
+
78
+ ### CLI
79
+
80
+ ```bash
81
+ # Via entry point (after install)
82
+ scrobbles top-albums overall --limit 25
83
+ scrobbles top-albums 2024
84
+ scrobbles dashboard
85
+
86
+ # Via main.py (development)
87
+ uv run python main.py top-albums overall --limit 25
88
+ uv run python main.py top-albums 2024
89
+ uv run python main.py top-albums last_month
90
+
91
+ # Export top albums to JSON
92
+ uv run python main.py dump-albums 2024 --limit 100
93
+
94
+ # Generate the HTML dashboard from existing JSON files
95
+ uv run python main.py dashboard
96
+
97
+ # Create Spotify playlists from all top100-YYYY.json files
98
+ uv run python main.py spotify-playlists --dry-run
99
+ uv run python main.py spotify-playlists --skip 2020 2021
100
+ ```
101
+
102
+ ### Python API
103
+
104
+ ```python
105
+ import scrobbles
106
+
107
+ # Recent tracks & now playing
108
+ scrobbles.get_recent_tracks("username", 10)
109
+ print(scrobbles.get_now_playing("username"))
110
+
111
+ # Top albums — predefined periods
112
+ scrobbles.print_top_albums("username", period="7day", limit=20)
113
+
114
+ # Top albums — arbitrary date ranges
115
+ scrobbles.print_top_albums("username", period="2024") # full year
116
+ scrobbles.print_top_albums("username", period="2024-06") # single month
117
+ scrobbles.print_top_albums("username", period="last_week")
118
+
119
+ # Export to JSON
120
+ scrobbles.dump_top_albums("username", period="2024", limit=100)
121
+
122
+ # Generate interactive HTML dashboard
123
+ from scrobbles.albums import build_dashboard
124
+ build_dashboard() # reads data/albums/top100-*.json → data/albums/index.html
125
+
126
+ # Spotify: create a playlist from any album list
127
+ from scrobbles.spotify import create_playlist_from_albums
128
+ albums = [{"artist": "Radiohead", "album": "OK Computer", "plays": 50}]
129
+ create_playlist_from_albums(albums, "My Playlist", dry_run=True)
130
+
131
+ # Spotify: bulk-create yearly playlists
132
+ from scrobbles.spotify import create_all_playlists
133
+ create_all_playlists(dry_run=True)
134
+ ```
135
+
136
+ ### Supported Period Strings
137
+
138
+ | Period | Description |
139
+ |---|---|
140
+ | `overall` | All time |
141
+ | `7day`, `1month`, `3month`, `6month`, `12month` | Rolling windows |
142
+ | `today`, `yesterday`, `last_week`, `last_month`, `last_year` | Calendar-relative |
143
+ | `2024` | Full calendar year |
144
+ | `2024-06` | Single month |
145
+ | `2024-06-15` | Single day |
146
+
147
+ ## Project Structure
148
+
149
+ ```
150
+ lastfm/
151
+ ├── src/scrobbles/
152
+ │ ├── __init__.py # Network setup, track utils, re-exports
153
+ │ ├── albums.py # Top albums: fetch, format, export, dashboard
154
+ │ ├── cli.py # CLI entry point
155
+ │ └── spotify.py # Spotify playlist creation
156
+ ├── data/albums/ # Generated JSON + HTML dashboard
157
+ │ ├── top100-YYYY.json
158
+ │ └── index.html
159
+ ├── main.py # Thin shim (calls scrobbles.cli:main)
160
+ ├── pyproject.toml
161
+ └── README.md
162
+ ```
163
+
164
+ ## Publishing
165
+
166
+ ```bash
167
+ # Bump version
168
+ uv version --bump patch
169
+
170
+ # Build
171
+ uv build
172
+
173
+ # Publish to PyPI
174
+ uv publish
175
+ ```
@@ -0,0 +1,154 @@
1
+ # scrobbles
2
+
3
+ Fetch, export, and visualize [Last.fm](https://www.last.fm/) listening data — plus create Spotify playlists from your top albums.
4
+
5
+ ## Setup
6
+
7
+ ### Prerequisites
8
+
9
+ - Python 3.12+
10
+ - [uv](https://docs.astral.sh/uv/) (recommended) or pip
11
+
12
+ ### Install
13
+
14
+ ```bash
15
+ pip install scrobbles
16
+ # or
17
+ uv add scrobbles
18
+ ```
19
+
20
+ #### From source
21
+
22
+ ```bash
23
+ git clone https://github.com/saforem2/scrobbles.git
24
+ cd lastfm
25
+ uv sync
26
+ ```
27
+
28
+ ### Environment Variables
29
+
30
+ #### Last.fm
31
+
32
+ Get API credentials at <https://www.last.fm/api/account>.
33
+
34
+ ```bash
35
+ export LASTFM_API_KEY="..."
36
+ export LASTFM_API_SECRET="..."
37
+ export LASTFM_USERNAME="..."
38
+ export LASTFM_PASSWORD="..."
39
+ ```
40
+
41
+ #### Spotify (optional — only needed for playlist creation)
42
+
43
+ 1. Create an app at <https://developer.spotify.com/dashboard>
44
+ 2. Add `http://127.0.0.1:8888/callback` as a Redirect URI in your app settings
45
+ 3. Export your credentials:
46
+
47
+ ```bash
48
+ export SPOTIFY_CLIENT_ID="..." # or SPOTIPY_CLIENT_ID
49
+ export SPOTIFY_CLIENT_SECRET="..." # or SPOTIPY_CLIENT_SECRET
50
+ export SPOTIPY_REDIRECT_URI="http://127.0.0.1:8888/callback"
51
+ ```
52
+
53
+ Either `SPOTIFY_*` or `SPOTIPY_*` naming works — the library maps them automatically.
54
+
55
+ ## Usage
56
+
57
+ ### CLI
58
+
59
+ ```bash
60
+ # Via entry point (after install)
61
+ scrobbles top-albums overall --limit 25
62
+ scrobbles top-albums 2024
63
+ scrobbles dashboard
64
+
65
+ # Via main.py (development)
66
+ uv run python main.py top-albums overall --limit 25
67
+ uv run python main.py top-albums 2024
68
+ uv run python main.py top-albums last_month
69
+
70
+ # Export top albums to JSON
71
+ uv run python main.py dump-albums 2024 --limit 100
72
+
73
+ # Generate the HTML dashboard from existing JSON files
74
+ uv run python main.py dashboard
75
+
76
+ # Create Spotify playlists from all top100-YYYY.json files
77
+ uv run python main.py spotify-playlists --dry-run
78
+ uv run python main.py spotify-playlists --skip 2020 2021
79
+ ```
80
+
81
+ ### Python API
82
+
83
+ ```python
84
+ import scrobbles
85
+
86
+ # Recent tracks & now playing
87
+ scrobbles.get_recent_tracks("username", 10)
88
+ print(scrobbles.get_now_playing("username"))
89
+
90
+ # Top albums — predefined periods
91
+ scrobbles.print_top_albums("username", period="7day", limit=20)
92
+
93
+ # Top albums — arbitrary date ranges
94
+ scrobbles.print_top_albums("username", period="2024") # full year
95
+ scrobbles.print_top_albums("username", period="2024-06") # single month
96
+ scrobbles.print_top_albums("username", period="last_week")
97
+
98
+ # Export to JSON
99
+ scrobbles.dump_top_albums("username", period="2024", limit=100)
100
+
101
+ # Generate interactive HTML dashboard
102
+ from scrobbles.albums import build_dashboard
103
+ build_dashboard() # reads data/albums/top100-*.json → data/albums/index.html
104
+
105
+ # Spotify: create a playlist from any album list
106
+ from scrobbles.spotify import create_playlist_from_albums
107
+ albums = [{"artist": "Radiohead", "album": "OK Computer", "plays": 50}]
108
+ create_playlist_from_albums(albums, "My Playlist", dry_run=True)
109
+
110
+ # Spotify: bulk-create yearly playlists
111
+ from scrobbles.spotify import create_all_playlists
112
+ create_all_playlists(dry_run=True)
113
+ ```
114
+
115
+ ### Supported Period Strings
116
+
117
+ | Period | Description |
118
+ |---|---|
119
+ | `overall` | All time |
120
+ | `7day`, `1month`, `3month`, `6month`, `12month` | Rolling windows |
121
+ | `today`, `yesterday`, `last_week`, `last_month`, `last_year` | Calendar-relative |
122
+ | `2024` | Full calendar year |
123
+ | `2024-06` | Single month |
124
+ | `2024-06-15` | Single day |
125
+
126
+ ## Project Structure
127
+
128
+ ```
129
+ lastfm/
130
+ ├── src/scrobbles/
131
+ │ ├── __init__.py # Network setup, track utils, re-exports
132
+ │ ├── albums.py # Top albums: fetch, format, export, dashboard
133
+ │ ├── cli.py # CLI entry point
134
+ │ └── spotify.py # Spotify playlist creation
135
+ ├── data/albums/ # Generated JSON + HTML dashboard
136
+ │ ├── top100-YYYY.json
137
+ │ └── index.html
138
+ ├── main.py # Thin shim (calls scrobbles.cli:main)
139
+ ├── pyproject.toml
140
+ └── README.md
141
+ ```
142
+
143
+ ## Publishing
144
+
145
+ ```bash
146
+ # Bump version
147
+ uv version --bump patch
148
+
149
+ # Build
150
+ uv build
151
+
152
+ # Publish to PyPI
153
+ uv publish
154
+ ```
@@ -0,0 +1,4 @@
1
+ from scrobbles.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "scrobbles"
7
+ version = "0.1.0"
8
+ description = "Fetch, export, and visualize Last.fm listening data — plus create Spotify playlists from your top albums"
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Sam Foreman", email = "saforem2@gmail.com" }]
13
+ keywords = ["lastfm", "last.fm", "scrobble", "spotify", "music", "audioscrobbler"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Topic :: Multimedia :: Sound/Audio",
20
+ ]
21
+ dependencies = [
22
+ "pylast>=7.0.2",
23
+ "spotipy>=2.26.0",
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/saforem2/scrobbles"
28
+ Repository = "https://github.com/saforem2/scrobbles"
29
+ Issues = "https://github.com/saforem2/scrobbles/issues"
30
+
31
+ [project.scripts]
32
+ scrobbles = "scrobbles.cli:main"
33
+
34
+ [dependency-groups]
35
+ dev = [
36
+ "bpython>=0.26",
37
+ "ptipython>=1.0.1",
38
+ "pyright>=1.1.408",
39
+ "ruff>=0.15.6",
40
+ ]