From 080814d83923fcc1697ce6d31cd11f1d95de13b5 Mon Sep 17 00:00:00 2001 From: Colin Powell Date: Fri, 7 Aug 2026 21:54:24 -0400 Subject: [PATCH] Split version history out of PROJECT.org into CHANGELOG.org --- AGENTS.md | 25 +- CHANGELOG.org | 2930 +++++++++++++++++++++++++++++++++++++++++ PROJECT.org | 3101 +------------------------------------------- scripts/release.py | 187 +-- 4 files changed, 3035 insertions(+), 3208 deletions(-) create mode 100644 CHANGELOG.org diff --git a/AGENTS.md b/AGENTS.md index 887462b..a8d0ae5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,23 +1,12 @@ -This is a Django-based web application that has an API, but primarily functions -with traditional Django views with HTML templates to display data that mostly -constitutes "scrobbled" items. The app started as a way to track a user's -watched videos via a Jellyfin server, but has since grown to keep track of a -number of media types: music tracks, tasks, videos, web pages, food, life -events, sports events, podcasts, video games, board games, beers, brick (lego) -sets, puzzles, books and geolocations. +This is a Django-based web application that has an API, but primarily functions with traditional Django views with HTML templates to display data that mostly constitutes "scrobbled" items. The app started as a way to track a user's watched videos via a Jellyfin server, but has since grown to keep track of a number of media types: music tracks, tasks, videos, web pages, food, life events, sports events, podcasts, video games, board games, beers, brick (lego) sets, puzzles, books and geolocations. -The project is written in Python and prefers to use "fat" models where logical -methods are contained in either instance methods on instatiated data models, or -classmethods on the Django model class itself. When logic grows too complex, -helper functions should be pulled out into utils.py files and the model instance -ro class method should call the utility function. +The project is written in Python and prefers to use "fat" models where logical methods are contained in either instance methods on instatiated data models, or classmethods on the Django model class itself. When logic grows too complex, helper functions should be pulled out into utils.py files and the model instance ro class method should call the utility function. -Be sure to check pyproject.toml for project defaults. Specifically for black and -isort expectations. +Be sure to check pyproject.toml for project defaults. Specifically for black and isort expectations. Imports in python files should always be top level if possible. -All tasks live in the PROJECT.org file and include an org ID that is a uuid to make them unique. +All tasks live in the PROJECT.org file and include an org ID that is a uuid to make them unique. Release history lives in CHANGELOG.org. In local development, environment variables for various sensitive values live in a .envrc file @@ -27,10 +16,8 @@ Care should be taken when using .envrc that we do not spam services we use in pr USING SESH COMMIT -This repository should use the `sesh` command to capture agentic session notes in git notes. You can -read the help next by running `sesh` by itself, but make sure that we use capture any reasoning -or prompts used for the work. +This repository should use the `sesh` command to capture agentic session notes in git notes. You can read the help next by running `sesh` by itself, but make sure that we use capture any reasoning, tools or prompts used for the work. Any time we say "commit" in this project, we mean "sesh commit" When asking to "commit" we should always assume we're using `sesh commit` and capturing session notes. -Release commits (`[release] Bump to version X.Y`) are handled by the user. Never auto-commit release steps; only prepare the changes (e.g. the PROJECT.org DONE/version entry and pyproject.toml bump) and leave them for the user to commit. +Release commits (`[release] Bump to version X.Y`) are handled by the user. Never auto-commit release steps; only prepare the changes (e.g. the PROJECT.org DONE entry, a new CHANGELOG.org version entry) and leave them for the user to commit. diff --git a/CHANGELOG.org b/CHANGELOG.org new file mode 100644 index 0000000..e650c9b --- /dev/null +++ b/CHANGELOG.org @@ -0,0 +1,2930 @@ +#+title: CHANGELOG + +* Version 64.5 [1/1] +** DONE [#B] Clean up issues with org-mode notes :orgmode:notes:scrobbles: +:PROPERTIES: +:ID: aa3dae85-b3ea-49a8-a964-0418595de0d2 +:END: + +*** Description + +When syncing notes from org mode scrobbles, the header of the last note gets +pulled into the new one, so on a scrobble with multple notes "- Note taken on" +with a bunch of newlines ends up in the second comment. + +Second, if an org_id is detected, like this: +#+begin_src text +[[id:f6fc4950-07e6-459b-a256-c18282ca9256][Mobile Transition Plan: Cookbooks V3 Nested → Flattened Fields]] +#+end_src + +The text should be transformed into human readable markdown text like this: + +#+begin_src md +[Mobile Transition Plan: Cookbooks V3 Nested → Flattened Fields](id:f6fc4950-07e6-459b-a256-c18282ca9256) +#+end_src + +Also, source blocks from org mode should be converted to markdown source block. + + +* Version 64.4 [1/1] +** DONE [#A] Fix raw HTML output from agent response :agents:bug: +:PROPERTIES: +:ID: e9137445-4a6e-4394-a31e-5b0767df62cd +:END: + +* Version 64.3 [2/2] +** DONE [#B] Add notifications when agent prompts return responses :notifications:agents: +:PROPERTIES: +:ID: 339fa174-b50c-8033-88cb-6984ec62274f +:END: +** DONE [#A] Make agent prompts more resilient :agents:bug: +:PROPERTIES: +:ID: 514ea328-a02a-a950-0fbe-445a66577675 +:END: + +* Version 64.2 [1/1] +** DONE [#B] Fix a bug in koreader imports using a static tmp file :bug:koreader:importers: +:PROPERTIES: +:ID: 31dad69f-33b8-71aa-fef0-91c088fb7bb9 +:END: + +* Version 64.1 [2/2] +** DONE [#B] Add search for agent sessiosn :agents:search: +:PROPERTIES: +:ID: a050cc92-9f7b-7df0-d6a4-3c03ab7e5a25 +:END: +** DONE [#A] Add rc.d files for FreeBSD process management :admin: +:PROPERTIES: +:ID: 7d67c042-cfd5-e2f0-d714-03a20f16803f +:END: + +* Version 64.0 [2/2] +** DONE [#B] Add Google Gemini as a model option back for agent sessions :agents: +:PROPERTIES: +:ID: 0adecc94-2ce2-ed8f-4439-0738ea409c68 +:END: +** DONE [#A] Update agent flow to use OpenRouter and select model :agents: +:PROPERTIES: +:ID: 70f753a8-b320-4f35-1dcd-9f0d4f1c707b +:END: + +*** Description + +Currently the agent session is limited to only Google Gemini 3.5. We should +instead do something like how Foods works, where when a user enters a prompt +the next page should allow choosing one of the free models pulled from OR. + +From that point on, the scrobbles is of that agent model and the response should +come from there, with any susequent prompts going to thes ame model (with +context of past prompts and response). + +**** Tasks +- [X] Add openrouter_agent_prompt and list_openrouter_free_models to agents/providers.py +- [X] AgentModelSelectView lists free OpenRouter models with the pending prompt +- [X] AgentScrobbleFromModelView scrobbles the chosen model as an agent session +- [X] ManualScrobbleView routes unprefixed prompts to the model select page via session +- [X] scrobble_agent_session_prompt task passes the session model to the provider +- [X] Keep gemini and opencode provider routes working during the transition +- [X] Update settings, conf example and .envrc for OpenRouter +- [X] Write tests + + +* Version 63.4 [1/1] +** DONE [#A] Update agent flow to use OpenRouter and select model :agents: +:PROPERTIES: +:ID: 70f753a8-b320-4f35-1dcd-9f0d4f1c707b +:END: + +*** Description + +Unprefixed prompts in the scrobble bar no longer hit Gemini directly. They now +redirect to an agent model select page (like Foods search) that lists free +OpenRouter models (priced $0 for both prompt and completion) pulled from the +OpenRouter models API. Picking one scrobbles that model as an agent session with +the prompt as the title; follow-ups keep using the same model with conversation +context. gemini and opencode provider routes remain for the transition. + +**** Tasks +- [X] Add openrouter_agent_prompt and list_openrouter_free_models to agents/providers.py +- [X] AgentModelSelectView lists free OpenRouter models with the pending prompt +- [X] AgentScrobbleFromModelView scrobbles the chosen model as an agent session +- [X] ManualScrobbleView routes unprefixed prompts to the model select page via session +- [X] scrobble_agent_session_prompt task passes the session model to the provider +- [X] Keep gemini and opencode provider routes working during the transition +- [X] Update settings, conf example and .envrc for OpenRouter +- [X] Write tests + +* Version 63.3 [1/1] +** DONE [#A] Fix overuse of OSM API on reverse geocodes :bug:geolocations: +:PROPERTIES: +:ID: f8be6925-7d67-4ee4-9321-c570b4888f14 +:END: + +*** Description + +Nominatim rate limits (HTTP 429) were permanently dropping reverse geocodes +because the task swallowed the error and never retried, and nothing enforced +Nominatim's 1 req/sec policy. This adds a typed NominatimRateLimited exception, +a cross-worker redis throttle enforcing 1 req/sec, and Celery retry honoring +Retry-After with exponential backoff. Also added a reverse_geocode_missing +management command and switched the admin action to enqueue tasks. + +**** Tasks +- [X] Add NominatimRateLimited exception and redis throttle to locations/utils.py +- [X] Retry reverse_geocode_geolocation with Retry-After / exponential backoff +- [X] Add reverse_geocode_missing management command +- [X] Admin action enqueues tasks instead of blocking with a sleep +- [X] Add GEOLOC_REVERSE_GEOCODE_RATE / GEOLOC_REVERSE_GEOCODE_MAX_RETRIES settings +- [X] Write tests + +* Version 63.2 [1/1] +** DONE [#A] Swap around AI API key for Google so Youtube still works :bug:agents: +:PROPERTIES: +:ID: e3fc73ee-d310-b2e6-e0f1-36e92cbf94c3 +:END: + + +* Version 63.1 [1/2] +** DONE [#A] Fix agent scrobble bug with duration :scrobbles:agents: +:PROPERTIES: +:ID: 84704fe9-57bf-f02d-b483-5177d2930662 +:END: +* Version 63.0 [3/3] +** DONE [#A] Add agent-session scrobbling :scrobbles:agents: +:PROPERTIES: +:ID: 0114a3d8-b13e-bbb6-b461-c7005e9be680 +:END: + +*** Description + +What if we could "scrobble" an agent session? Honestly, this is mostly applicable to +web searches or quick questions via Gemini or similar web-based agents. + +The scrobble should be the tool, so using Gemini is an agent_session scrobble, and the +associated prompt and any response are stored in the AgentSessionLogData. For this, +we'd need a way to start a prompt with an AI agent via python libraries like genai. + +Then we'd ask a question or a prompt request in the top Scrobble something bar. In +this case, an entry without a - prefix would be understood as starting a +Gemini chat and would send the prompt to Gemini, get a response and return it +to the browser, starting a new scrobble and storing any response in the log data. + +Then the AgentSessionLogData would capture a list of turns: [{"prompt_id": , +"prompt": "A request prompt", "response": "An agent respones"}, + +**** Tasks +- [X] Add agents app with AgentSession model and AgentSessionLogData +- [X] Add gemini (httpx) and opencode (CLI) agent provider modules +- [X] Wire AgentSession into scrobbles (media type, FK, prefetching) +- [X] Route unprefixed manual scrobble input to agent sessions +- [X] Add async Celery task to fetch turn responses +- [X] Render agent turns on scrobble detail with htmx polling +- [X] Add agent session list/detail views and templates +- [X] Generate migrations +- [X] Write tests + +** DONE [#A] Auto-complete agent session scrobbles after 10 minutes :scrobbles:agents: +:PROPERTIES: +:ID: b4863178-7598-4bb7-9df7-6635d4851b57 +:END: + +*** Description + +Agent session scrobbles should start in progress and stay that way after the +turn response arrives so the session is visible as "Asking now" on the +homepage. Roughly ten minutes after the final turn's response, a Celery task +auto-completes the scrobble (in_progress = False). If a follow-up is submitted +while a turn is still pending, the auto-complete re-schedules itself. + +**** Tasks +- [X] Keep agent session scrobbles in progress after turn responses +- [X] Add scrobble_agent_session_complete Celery task (10 min auto-complete) +- [X] Re-schedule auto-complete when a turn is still pending +- [X] Write tests + +** DONE [#A] Fix small bug in nature importer if geojson is missing :importers:nature: +:PROPERTIES: +:ID: 0e0a9089-fda1-5e0d-6974-bebacb571cd3 +:END: + +* Version 62.2 [3/3] +** DONE [#A] Fix concurrent trend to be more useful :trends:concurrent: +:PROPERTIES: +:ID: b43652a8-31e3-6e0d-8489-5cc93864df84 +:END: +** DONE [#B] Add a public blog feature for scrobbles with notes :blog: +:PROPERTIES: +:ID: 0ae9449a-e107-39dd-cd47-7174021b5c18 +:END: +** DONE [#A] Add drf-spectacular for OpenAPI schema generation :api:documentation: +:PROPERTIES: +:ID: 2fddd5d0-1138-4a6e-96b7-5879035f033f +:END: + +*** Description + +Install drf-spectacular and configure it to generate an OpenAPI 3.0 schema for +all 48+ REST endpoints. This is a prerequisite for auto-generating the Kotlin +networking layer. Add the schema endpoint at /api/schema/ and the Swagger UI +at /api/docs/. + +**** Tasks +- [X] Add drf-spectacular to dependencies +- [X] Configure in REST_FRAMEWORK settings +- [X] Add schema and docs URL patterns +- [X] Verify schema generates cleanly for all ViewSets +- [X] Fix any serializer issues that block schema generation + + +* Version 62.1 [1/1] +** DONE Add open food stats as requirement +:PROPERTIES: +:ID: c6f47c5a-2c24-9e48-f1e5-edd11be5f36c +:END: + +* Version 62.0 [1/1] +** DONE [#B] Add open food facts source for Food and Drink :source:food:drinks: +:PROPERTIES: +:ID: d8a3e498-b03c-050d-a9bf-86e76eaa9201 +:END: + +* Version 61.5 [2/2] +** DONE [#A] Fix referer bug in OSM tiles :bug:osm:templates: +:PROPERTIES: +:ID: dcfbe119-d761-b0a4-fb03-6ce920fda6bf +:END: +** DONE [#A] Fix bugs in lifeevent urls :bug:urls: +:PROPERTIES: +:ID: ac6c0102-4de7-b6e4-65de-8231752d9a31 +:END: + +* Version 61.4 [1/1] +** DONE [#B] Add 7-day period for trends :trends: +:PROPERTIES: +:ID: 95731744-b0ec-175e-88dc-822cf9110292 +:END: + +* Version 61.3 [1/1] +** DONE [#A] Add trends tests and fix fasting :trends:tests:fasting: +:PROPERTIES: +:ID: 70c20539-110e-d4f4-30bd-e245152459c2 +:END: + +* Version 61.2 [1/1] +** DONE Fix bug in trends and add liquid fasting :food:fasting:trends: +:PROPERTIES: +:ID: 4736676e-95b0-cb4c-0eaa-c151f3753cd0 +:END: + +* Version 61.1 [1/1] +** DONE [#B] Add trend for fasting :food:fasting:trends: +:PROPERTIES: +:ID: a290b64d-14ad-49c2-addc-0d149c0c38fc +:END: + +*** Description + +Could we add a trend for number of fasting days over the time period? This would +look for the gap between the last and next Food scrobble and if it's ever longer +than a specified seconds configuration setting we'd mark it as a fasting day. +There can be "Periodic Fasting" which is defined by default as 16 hours or "Full +Fasting" which is designed as a 24 hour period without fasting. + +Fasting checking should also be skipped if it's during a LifeEvent of type +Vacation as vacations should be exempt. This exemption should be a userprofile +setting so folks can turn it on or off, and if it's changed the next trend +rebuild should consider that. + + + +* Version 61.0 [3/3] +** DONE [#B] Try adding a nature app with Observations and Species +:PROPERTIES: +:ID: 9551cf06-02b7-1bc9-3600-b0407ecc5967 +:END: + +*** Description + +iNaturalist has a feed in atom. + +** DONE [#A] Add ability to push one or sync all board game scrobbles to BGG :boardgames: +:PROPERTIES: +:ID: 1f306552-35e8-4a75-a661-b0956e8de967 +:END: + +*** Description + +We have a half-baked BGG sync module. I don't even know it if works. Can we +clean it up, add some tests and then add UI features to either sync a single +board game scrobble (via the detail page) to BGG, along with a "sync all" button +on the board game list page. Additionally, the sync should merge data between +Vrobbler and BGG based on timestamp and bgg_id. Priority should be given to data +from Vrobbler. + +** DONE [#C] Clean up naming of =bgsplay= parsing :importers:refactoring: +:PROPERTIES: +:ID: c751dbbc-464a-4e63-9fe3-e034303f7b54 +:END: + +*** Description + +We should rename `email_scrobble_board_game` to reflect the fact that it's just +a helper method to create board game scrobbles given a json blob. It's +independent of the email flow it was originally creatdd for + + +* Version 60.2 [1/1] +** DONE Use FastCork to lookup wine data :drinks:wine:metadata: +:PROPERTIES: +:ID: add9a951-d957-415d-b41e-feee40479774 +:END: + +* Version 60.1 [1/1] +** DONE [#B] Migrate coffee scrobbles to new Coffee model :drinks:mgmtcmd: +:PROPERTIES: +:ID: edaf1200-b58e-8512-b2a2-c8bc25bed7a0 +:END: + +*** Description + +Food ID 10 is "French Press Coffee with Sugar" and we should convert all of +these scrobbles to scrobbles of a Coffee, but to figure out which coffee, we can +use scrobbles of Task "Chore" where the title is "Roast coffee" and the notes +are not null. In this case, the first note on each one has a Coffee title like +"Mexico - Veracruz Huatusco" where the Origin is Mexico and the title is +"Veracruz Huatusco" + +We should create a new Coffee for each Chore with the title of "Roast Coffee" +and then for each "French Press Coffee with Sugar", swap the Food ID 10 for the +Coffee ID of the latest Coffee created from a Chore. If a new Chore is found, +start updating the French Press scrobbles with a newly created Coffee. + +If we don't have a roast log in the Chore tasks before a French Press entry, we should use the "Unknown Roast" Coffee title and skip all the othe metadata. + + +* Version 60.0 [2/2] +** DONE [#B] Send water drinking notifications :drinks:notifications: +:PROPERTIES: +:ID: 6c0db717-a0c7-34a1-57e1-941db0188875 +:END: +** DONE [#B] Add wine as a ScrobbleItem in a drinks app and move beers into drinks app :drinks:beer: +:PROPERTIES: +:ID: add1be0d-eb33-3273-37bf-0f2cab7e67c5 +:END: + +* Version 59.5 [1/1] +** DONE [#A] Fix bug where all variants for board games are in form :boardgames: +:PROPERTIES: +:ID: 9c4cd193-580a-5b33-8832-1feffea7bd53 +:END: + +* Version 59.4 [2/2] +** DONE Fix bug in fetching expansions for board games :boardgames: +:PROPERTIES: +:ID: 17995312-e76e-4a50-b591-0eab78cb59ab +:END: + +#+begin_src python + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/boardgames/management/commands/fetch_expansions.py", line 60, in handle + fetch_and_link_expansions(game, expansions) + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/boardgames/utils.py", line 51, in fetch_and_link_expansions + expansion = BoardGame.find_or_create(str(exp_id)) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/boardgames/models.py", line 409, in find_or_create + bgg_data = lookup_boardgame_from_bgg(lookup_id=lookup_id) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/boardgames/sources/bgg.py", line 15, in lookup_boardgame_from_bgg + game = bgg.game(game_id=lookup_id) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/boardgamegeek/api.py", line 1045, in game + raise BGGApiError(msg) +boardgamegeek.exceptions.BGGApiError: invalid data for game id: 242117 +#+end_src + +** DONE Board games should have genres extracted from family data :boardgames:metadata: +:PROPERTIES: +:ID: 7214b270-dccc-4b98-ac58-ff4f76c8cda9 +:END: + + +* Version 59.3 [2/2] +** DONE Exclude some board games from auto-expansion imports :boardgames: +:PROPERTIES: +:ID: 51ffdf20-e732-4774-b781-c3501d26d46f +:END: + +*** Description + +Some board games, especially trading card games, have silly amounts of expansions. +We should have a setting SKIP_AUTO_EXPANSION_DOWNLOAD that is a list of BGG ids of +games where expansions should not be download automatically. This exclusion should also auto-include +any games with "Collectible Card Games" in it's family. + +** DONE Should be able to add new variants to board games via the log data form :boardgames: +:PROPERTIES: +:ID: 5ed0dd25-3026-3da8-dc5c-f2a75751af9a +:END: + +* Version 59.2 [1/1] +** DONE Fix test failure in discgolf app :discgolf:tests: +:PROPERTIES: +:ID: 813ae357-0568-5a4c-1a35-172e95d02740 +:END: + +* Version 59.1 [1/1] +** DONE Fix bug when expansions have no image :boardgames:bug: +:PROPERTIES: +:ID: 9fee96c9-c6a0-32d9-b6f8-212c60fc3540 +:END: + +* Version 59.0 [3/3] +** DONE [#A] Add BoardGameVariant model :boardgames: +:PROPERTIES: +:ID: 0ffb20d5-252f-b13d-473d-5529014602ff +:END: + +*** Description + +Variants represent unique boards being used per scrobble or scenarios when +playing a game. Scrobbles of a board game may have one or more +boardgame_variant_ids assocaited with their log data, and a variant is created +for one specific board game. + + +** DONE [#A] Lookup all Expansions for a game when creating it :boardgames: +:PROPERTIES: +:ID: 8a84b06d-555c-4701-9058-ff364c89c198 +:END: + +*** Description + +We don't want to blow up the BGG API, but if possible with not too +many calls, when we scrobble a board game, in order to allow +populating the "Expansions" multi select, we should fetch any +expansions for the board game when creating it for the first time. + +We should also create a managemnt script to update existing board games. + +** DONE [#A] Board game expansion lookup should be async on URL scrobbles :boardgames: +:PROPERTIES: +:ID: 968d8dde-f906-cdf0-af4e-b87ce28ddbbb +:END: + +* Version 58.8 [1/1] +** DONE [#B] Clean up trend templates :trends:templates: +:PROPERTIES: +:ID: 83237e2c-857b-47c9-c86c-32a5e3f1359d +:END: + +* Version 58.7 [2/2] +** DONE [#B] Split up chart page between tables and maloja :charts:templates: +:PROPERTIES: +:ID: 103ab084-2016-cfa4-c677-3c5fdc54cce0 +:END: +** DONE [#A] Fix CI so we don't double run deploys and builds :ci: +:PROPERTIES: +:ID: 1a93e7cb-b883-aae5-2bd5-fcdd6e16f8ab +:END: + +* Version 58.6 [1/1] +** DONE [#B] Cleanup commands should check for broken images :metadata:cleanup: +:PROPERTIES: +:ID: bacce321-73c7-ae1f-bfa7-c3ee517b5441 +:END: + +* Version 58.5 [1/1] +** DONE [#A] The maloja style charts are messed up :templates:charts: +:PROPERTIES: +:ID: 987397a2-7e74-4eb1-87cc-4c8bbe1c7b23 +:END: + +* Version 58.4 [2/2] +** DONE [#B] Allow people all trends or individual trends :trends:profiles: +:PROPERTIES: +:ID: 1d081152-abd1-73c2-a625-903565a10c6c +:END: +** DONE [#A] Fix a bug in board game scorelog data :boardgames:logdata: +:PROPERTIES: +:ID: 014bab30-13bf-fae7-e678-4666a8d38ae4 +:END: + +* Version 58.3 [1/1] +** DONE [#A] Remove curl-cffi as it doesn't work on FreeBSD :webpages:deps: +:PROPERTIES: +:ID: 6bc1b0dd-e449-3d32-a176-46451e793e5d +:END: + +* Version 58.2 [2/2] +** DONE [#B] Add more robust webpage scraping :webpages:metadata: +:PROPERTIES: +:ID: 84d9bfa5-75c0-0718-764e-379f7456602a +:END: +** DONE [#B] Time of Day Categories trend :trends: +:PROPERTIES: +:ID: 6598074f-2290-46db-967b-29f45d30be29 +:END: + +*** Description + +Added a "Time of Day Categories" trend that groups scrobbles for Books, Trails, +Birding Locations, and Board Games into Early Bird (5-10:59am), Day Jay (11am-6:59pm), +and Night Owl (7pm-4:59am) buckets. Shows both overall and per-media-type breakdowns. + + +* Version 58.1 [1/1] +** DONE [#B] Add auto genre tagging for papers :books:papers:metadata: +:PROPERTIES: +:ID: e6b5c3a5-7fc6-b530-96c2-b5962a716db6 +:END: + +* Version 58.0 [1/1] +** DONE [#B] Add scrobbling of Papers via webpages with doi.org links in them :feature:papers: +:PROPERTIES: +:ID: d30bb8aa-eefd-002c-38d5-3f2fcef345f2 +:END: + +* Version 57.1 [1/1] +** DONE [#A] Write poetry lock file :bug:deps: +:PROPERTIES: +:ID: 0f5a6f4b-a486-ba7e-bbce-f7581274398c +:END: + +* Version 57.0 [5/5] +** DONE [#A] Scrobble button on some media list pages dont work :bug:scrobbles: +:PROPERTIES: +:ID: a3a5c707-2e3d-a6b1-0f7f-4c6f7433aa1f +:END: +** DONE [#B] Use HTMx to update the Now Playing widget :feature:templates: +:PROPERTIES: +:ID: 5f5631fc-9ee1-d5a5-d0f8-94fea6fbbfa4 +:END: +** DONE [#B] Add a live page that updates the scrobble list via JS polling :feature:templates: +:PROPERTIES: +:ID: 58790d76-dc6e-8aa5-2dc0-e64fe786fbf1 +:END: +** DONE [#A] Turns out we cant cache the now playing widget :bug:templates: +:PROPERTIES: +:ID: 9ce669ea-c000-cdfe-a634-ad5cdaeae81c +:END: +** DONE [#C] What would it look like to add an MCP server to expose scrobbles and media items? :mcpserver:feature: +:PROPERTIES: +:ID: c5fca159-c7e0-5795-7c05-bbc48f539650 +:END: + +* Version 56.4 [3/3] +** DONE [#B] Add ability to do reverse address lookup on lat-long pairs :geolocations:feature: +:PROPERTIES: +:ID: 86c071ff-7638-41ba-6b65-1382df1cb5aa +:END: +** DONE [#B] Add address fields to GeoLocation :addresses:geolocation: +:PROPERTIES: +:ID: a55ae508-07ab-ccdd-e453-846bd3fca6fb +:END: +** DONE [#B] Add better detail template for Disc Golf Courses :discgolf:templates: +:PROPERTIES: +:ID: 12cee67c-f723-0fa3-848a-cbc6e4d65fc3 +:END: + +* Version 56.3 [1/1] +** DONE [#A] Fix bug in importer script from discgolf being added :bug: +:PROPERTIES: +:ID: c3733f96-18f1-eef8-f5d9-edaf97e35623 +:END: + +* Version 56.2 [1/1] +** DONE [#A] Fix bug in creating people when importing course plays :discgolf:bug: +:PROPERTIES: +:ID: 255e9886-098b-39ae-1077-25e43223660e +:END: + +* Version 56.1 [1/1] +** DONE [#A] Add tests to discgolf app :discgolf:tests: +:PROPERTIES: +:ID: 28e8344e-c3cf-19af-ce1c-cb821d4fcb5f +:END: + +* Version 56.0 [1/1] +** DONE [#B] Add DiscGolf as a scrobbleable media :discgolf: +:PROPERTIES: +:ID: 8cdde5d3-0ae5-7d5a-99d2-200c86afae03 +:END: + +*** Description + +I have a csv file fro the uDisc disc golf scoring app that looks like: + +Singles round. Note second row is the par for the course +#+begin_src csv +PlayerName,CourseName,LayoutName,StartDate,EndDate,Total,+/-,RoundRating,Hole1,Hole2,Hole3,Hole4,Hole5,Hole6,Hole7,Hole8,Hole9 +Par,DR Front 9,Custom Layout,2026-06-19 1535-0400,2026-06-19 1725-0400,27,,,3,3,3,3,3,3,3,3,3 +Colin Powell,DR Front 9,Custom Layout,2026-06-19 1535-0400,2026-06-19 1725-0400,30,3,,2,3,4,4,3,4,3,3,4 +Asa Sewell,DR Front 9,Custom Layout,2026-06-19 1535-0400,2026-06-19 1725-0400,44,17,,5,4,4,8,5,5,4,4,5 +Emma Sweet,DR Front 9,Custom Layout,2026-06-19 1535-0400,2026-06-19 1725-0400,41,14,,5,4,5,6,3,4,3,5,6 +Jane Sewell,DR Front 9,Custom Layout,2026-06-19 1535-0400,2026-06-19 1725-0400,44,17,,4,5,5,5,5,5,4,6,5 +Nabby Sewell,DR Front 9,Custom Layout,2026-06-19 1535-0400,2026-06-19 1725-0400,59,32,,6,6,7,7,6,7,6,6,8 +Silas Sewell,DR Front 9,Custom Layout,2026-06-19 1535-0400,2026-06-19 1725-0400,41,14,,5,5,4,5,3,5,4,4,6` +#+end_src + +Teams of two or more persons. Note second row is the par for the course +#+begin_src csv +PlayerName,CourseName,LayoutName,StartDate,EndDate,Total,+/-,RoundRating,Hole1,Hole2,Hole3,Hole4,Hole5,Hole6,Hole7,Hole8,Hole9 +Par,Peninsula Links,Main,2026-06-19 2322-0400,2026-06-19 2323-0400,27,,,3,3,3,3,3,3,3,3,3 +Colin Powell + Asa Sewell,Peninsula Links,Main,2026-06-19 2322-0400,2026-06-19 2323-0400,29,2,,3,4,2,3,2,3,5,3,4 +Emma Sweet + Jane Sewell,Peninsula Links,Main,2026-06-19 2322-0400,2026-06-19 2323-0400,28,1,,4,3,4,2,3,4,3,3,2 +#+end_src + +We should add a new app called discgolf that has the following data models: + +- DiscGolfRound - scrobblable media + course_id, round_type (Singles, Teams) +- DiscGolfCourse - name, layoutname, number_of_holes + +And the logdata for a DiscGolfOuting scrobble should have: + + {person: {hole_number: score}, total: int} + + {team: {name: "", people: [person, person], hole_number: score}, total: int} + + weather + + fun_factor (miserable, not great, so-so, good, excellent, party time) + + +* Version 55.6 [1/1] +** DONE [#A] Figure out why historical Lastfm imports don't work :importers:lastfm:music: +:PROPERTIES: +:ID: 71b18c1b-de96-6d93-20fa-de2ec0df1288 +:END: + +* Version 55.5 [1/1] +** DONE [#B] Fix bug in lastfm import for new users :importers:lastfm:music: +:PROPERTIES: +:ID: d034966d-0c7f-e512-4cf8-7329c9026b6f +:END: + +* Version 55.4 [1/1] +** DONE [#A] Tighten up the speed of startup and first request :perf: +:PROPERTIES: +:ID: 9ee8834c-6be2-d04b-df6d-56375504083f +:END: + + +* Version 55.3 [3/3] +** DONE [#C] =alt_names= feature for artists (commented out / dead code) :music:dead-code: +:PROPERTIES: +:ID: e22060a2-5f7a-4f33-9056-309ecd27159c +:END: + +*** Description + +File: ~vrobbler/apps/music/models.py~ (line 236) + +An entire block of code for tracking alternate artist names is commented +out. The TODO questions whether it even works. Review: either implement +properly or remove the dead code. + +** DONE [#A] Put chart rebuilds in a lower priority task queue :charts:tasks: +:PROPERTIES: +:ID: 43c90de0-fc1c-1139-dac7-9b7c82006b2e +:END: +** DONE [#A] Check for existing book scrobble and update page count :books:scrobbling: +:PROPERTIES: +:ID: 1a0609bc-6b16-4da4-96c1-59588229e4b4 +:END: + +*** Description + +File: ~vrobbler/apps/scrobbles/scrobblers.py~ (line 330) + +When scrobbling a book (comic), the code doesn't check for prior scrobbles to +update reading progress. Needed for proper page-count tracking. + + +* Version 55.2 [2/2] +** DONE [#A] Fix bug in scrobble id in calendar view :templates: +:PROPERTIES: +:ID: 8cb34852-b18f-e794-cd9b-fb1ecad70a0d +:END: +** DONE [#A] Video game cleanup script should clear out broken images :metadata:videogames: +:PROPERTIES: +:ID: ca1f1ea9-0f79-082c-5ff7-867671faff4b +:END: + +* Version 55.1 [1/1] +** DONE [#A] Clean up metadata scrapping for video games :metadata:videogames: +:PROPERTIES: +:ID: fbc421b5-21a3-4aed-9062-c59192ead065 +:END: + +* Version 55.0 [3/3] +** DONE [#B] Use pk ID for scrobble detail view, not uuid :scrobbles: +:PROPERTIES: +:ID: 9cc3b285-e478-041e-394b-3d550aefbe1d +:END: +** DONE [#B] Display videogame screenshots on scrobble detail if they exist :videogames:templates: +:PROPERTIES: +:ID: 0406d082-20f6-0d12-76e2-f281c4801468 +:END: +** DONE [#B] Add autotagging to webpages based on domain, title :webpages:metadata: +:PROPERTIES: +:ID: f658435b-f7a0-42e6-b9f6-226678a77a55 +:END: + +*** Description + +For easier filtering, like we do with tasks, we should auto tag WebPage instances +based on the domain name split part by periods (so news.ycombinator.com tags: news, ycombinator, com) + +And also based on the nouns in the title. + + +* Version 54.5 [1/1] +** DONE Fix bug in generating mood trends :trends: +:PROPERTIES: +:ID: 8e75abfa-8e70-d85b-00a4-a4813bbce879 +:END: + +* Version 54.4 [2/2] +** DONE [#A] Remove all-time trends :trends: +:PROPERTIES: +:ID: 53b231d1-7677-8cd3-1d88-dae110aba1e6 +:END: + +*** Description + +All time trends take forever to calculate and don't provide too much data + +** DONE [#B] Add a trend around moods :moods:trends: +:PROPERTIES: +:ID: fba3f4ae-8f97-ee0b-e762-31630884518a +:END: + +* Version 54.3 [1/1] +** DONE [#B] Fix bug in series metadata cleanup script :videos:metadta: +:PROPERTIES: +:ID: 85448702-907c-5d63-f5af-7795661d7c46 +:END: + +* Version 54.2 [4/4] +** DONE [#B] Add script to clean up TV series metadata :videos:metadata: +:PROPERTIES: +:ID: a468b328-59d9-f84b-9ddb-087216783453 +:END: +** DONE [#A] Update youtube video detail pages with links to channel :videos:templates: +:PROPERTIES: +:ID: 8b87cb42-09e5-a3f5-136f-182f967fa81f +:END: +** DONE [#A] Concurrent reading trend does not consolidate on single book :trends:reading: +:PROPERTIES: +:ID: fe220f55-7e0d-2a17-2477-a5aa7c4a1f2c +:END: +** DONE [#B] Trends dont seem to look very far back :trends: +:PROPERTIES: +:ID: ffcfba3f-5a93-9ee0-9680-666e6eccd684 +:END: + +*** Description + +Specificially, looking at reading-pace when run on prod, it claims that I've +only had one reading session without music. Which may be true, but perhaps we +need to indicate what the time frame we're looking at is (month, week, year) +and provide a way to jump back and forward through time, same as charts. + + +* Version 54.1 [1/1] +** DONE [#A] Concurrent listening trend is inefficient and should be disabled :trends:scrobbles: +:PROPERTIES: +:ID: 4aa3b719-6b22-cae9-85f0-fac67b4fc753 +:END: + +* Version 54.0 [3/3] +** DONE [#B] Add peak hour, weekly rhythm and activity dist trends :trends:scrobbles: +:PROPERTIES: +:ID: 5fa52fac-d5f0-4369-bcaa-589c886b07d3 +:END: + +** DONE [#A] Implement YouTube channel info scraping :videos:youtube:stub: +:PROPERTIES: +:ID: 1d3beafd-62cb-4735-a465-edb37bf885db +:END: + +*** Description +File: ~vrobbler/apps/videos/models.py~ (line 140) + +=Video.fix_metadata()= is a stub that logs "Not implemented yet" and returns. +Needs actual implementation to scrape channel metadata from YouTube. + +** DONE [#A] Fix Amazon book scraper :amazon:scraper:broken: +:PROPERTIES: +:ID: c38aba25-0171-49ab-a9f3-acf2003da429 +:END: + +*** Description + +File: ~vrobbler/apps/books/amazon.py~ (line 56) + +The =scrape_data_from_amazon()= function is likely broken due to Amazon blocking +scrapers and changing HTML structure. Needs rewrite or replacement with a proper +API. + + +* Version 53.1 [1/1] +** DONE [#A] Error with loading logdict :scrobbles:bug:logdata: +:PROPERTIES: +:ID: 92d4fa16-4b90-47e0-95ae-472bdca582ce +:END: + + +* Version 53.0 [5/5] +** DONE [#B] Add a /trends/ page that shows trends based on scrobble data :feature:trends:scrobbles: +:PROPERTIES: +:ID: 03e9fe30-2bc6-4062-bb24-e95b98daf05b +:END: + +*** Description + +This project is a bit invovled. But we should add a top level URL `trends` that shows +various trends as defined either in a static settings file, or dynamically via a database table. + +Trends could be things like doing multiple things at the same time, like while driving, what +did we listen to this week, or while running, what were listening to this week? + +Or more complicated trends like, how time per page changes based on the book I was reading, or if I was doing something else (music or sport event) while reading. + +** DONE [#B] Notify users when Last.fm import completes :importers:notifications: +:PROPERTIES: +:ID: 92846b36-54c5-4b78-9c57-bdc401045fbe +:END: + +*** Description + +After a bulk import from Last.fm, users receive no confirmation. Should add a notification (in-app, email, or similar). + +File: ~vrobbler/apps/scrobbles/importers/lastfm.py~ (line 96) + +** DONE [#C] Cleaner =GeoLocationLogData= deserialization :models:refactoring: +:PROPERTIES: +:ID: 85465dbf-69b3-48cb-9df0-cd076c4470ab +:END: + +*** Description +Currently special-cases =GeoLocationLogData= by reaching into a nested ="movement_detection"= key. Should be handled at the LogData dataclass level. + +File: ~vrobbler/apps/scrobbles/models.py~ (line 977) + +** DONE [#B] Webpage scrobbles should diff existing webpages content :webpages:metadata: +:PROPERTIES: +:ID: 25576197-258f-48d6-bfe9-e4172a0a1898 +:END: + +*** Description + +Webpages change content between scrobbles. The current model stores the webpage content once, the +first time it's scrobbled. When a page has been seen before, we should move the existing content +to a new model HistoricalWebPage with the following fields: + +webpage_id -> FK to WebPage +date -> date from existing WebPage content +domain -> same as existing WebPage content +extract -> copy of existing WebPage content + +Once the HistoricalWebPage instance is successfully created, the new extract data +should be saved into the WebPage instance. + + +** DONE [#B] Make ArchiveBox push asynchronous :archivebox:async: +:PROPERTIES: +:ID: 17c116a7-5952-db37-e56c-2987c2fc456b +:END: +*** Description + +=push_to_archivebox()= runs synchronously during the request. Should be moved to a +Celery task or similar background worker. + +File: ~vrobbler/apps/webpages/models.py~ (line 133) + + +* Version 52.2 [1/1] +** DONE [#A] Fix bug in recomputing long play seconds taking forever :bug:longplay:commands: +:PROPERTIES: +:ID: 0a813cf9-17fb-dbd7-b5a7-7410d9bd4d8c +:END: + +* Version 52.1 [1/1] +** DONE [#C] Show time per scrobble in long play lists and total time playing :templates:longplay:scrobbles: +:PROPERTIES: +:ID: b3d16230-8ec5-46db-b166-59e98d0ee06c +:END: + +*** Description + +Long play time should be show in the table of scrobbles on a media detail page. +The total time spent in a long play that's either no completed yet or completed +should be displayed as well. If completed, the date finished should be shown as +well. + + +* Version 52.0 [5/5] +** DONE [#B] Allow marking media as long play complete from detail page :templates:scrobbles:longplay: +:PROPERTIES: +:ID: 2c314768-be97-9b10-d13c-9cfd0f38a64e +:END: +** DONE [#A] Fix how long play scrobbles are tracked :scrobbles:longplay:serial: +:PROPERTIES: +:ID: 908b0493-cabf-40c1-825f-cd59a8ad0f7a +:END: + +*** Description + +Currently we have this idea of "long_play" scrobbles but there's a lot missing +to tie it together. + +What we'd prefer is that when a new scrobble is added for a media_type that +`is_long_play` the most recent scrobble finished is added as the +`last_serial_scrobble` to the log data. But all the other long play stuff exsits +as data model fields. We should add `long_play_last_scrobble` as a FK to this +scrobble when creating a new longplay scrobble. + +Additionally, `long_play_seconds` we should have a recompute management command +to walk backward from `long_play_last_scrobble` until a `long_play_complete` +scrobble is found (exclusive) and save the time. + +We should also ony use `long_play_complete` field on the scrobble ... some +logdatas have a similar field, but we should make sure that we always use the +model field to determine if a long play is finished. + +This should include a command to clean up long play data to consolidate around +the `long_play_complete` field. + + +** DONE [#B] Paginate or limite scrobbles on media admin pages :admin:scrobbles:media: +:PROPERTIES: +:ID: f02e487b-d7ed-4834-838a-303560f2ad3b +:END: + +** DONE [#B] Clean up books admin :admin:books:bug: +:PROPERTIES: +:ID: 7539bee6-0a52-26f6-ebc6-5554ac49a716 +:END: +** DONE [#B] Clean up favorites admin :admin:favorites:scrobbles: +:PROPERTIES: +:ID: f2be0c69-1bf8-b5a3-5269-9c8ea873361d +:END: + + +*** Description + +Some FK lookups in admin should be raw_id_fields. + + + +* Version 51.4 [1/1] +** DONE [#A] Clean up metadata comicbook enrichment :bug:comics:books:metadata: +:PROPERTIES: +:ID: cd875450-7117-78ca-8be4-9c8b73037dba +:END: + +*** Description + +Still getting wonky results with some comicbooks. Would be nice to be able to +tag a Book as a comicbook, and also gather volume information. I also noticed +that some books that are found in OL never get their comicvine_id populated. We +should make sure we always have comicvine_ids if available. + + +* Version 51.3 [1/1] +** DONE [#A] Improve speed of index and chart pages :bug:scrobbles:perf: +:PROPERTIES: +:ID: 031a23f8-7c02-4926-9884-6654ceca16c2 +:END: + +*** Description + +Over the last few releases, the home page and charts pages have gotten really +slow. + +We should look into what's causing the slowness and maybe do more agressive +query optimization or caching. + + +* Version 51.2 [2/2] +** DONE [#A] Fix bug where last page of book gets separate scrobble :bug:books:importers:koreader: +:PROPERTIES: +:ID: e13e0b4c-461e-e5a9-c685-b972f4e262e5 +:END: + +*** Description + +The new KoReader code is working great to import books with the correct timezone +and what-not. But it has a weird artifact of creating one extra scrobble for the +last page read. Need to button that up. + +** DONE [#B] Fix metadata scraping for books :books:metadata: +:PROPERTIES: +:ID: ea416a69-a8a8-4d05-b7d4-0a3470820e34 +:END: + + +* Version 51.1 [1/1] +** DONE [#A] Fix scrobbling comic books :books:scrobbles:bug: +:PROPERTIES: +:ID: 8dfbff19-3fa4-f3b8-21c7-7a416498000c +:END: + +*** Description + +At some point logdata and log got confused, and now when you try +to scrobble a comic book, it just throws errors. We should look +into where the confusion happened and fix it. + + +* Version 51.0 [3/3] +** DONE [#B] Fix koreader scrobble imports to use DST properly :bug:books:imports: +:PROPERTIES: +:ID: 79758cba-a440-48b6-a637-efb88827acf2 +:END: + +- Note taken on [2025-09-25 Thu 10:37] \\ + + This may already be fixed ... need to check. + +- Note taken on [2025-02-25 12:34] \\ + + The page data has the canonical date something was read in it, but it seems + to be an hour off. I traced this back to being off during DST, so we just need + the importer to be aware of whether a user is using DST or not and roll back + an hour for part of the year. Also, we'd need to adjust any old scrobbles that + took place with DST off to roll them back by an hour. + + +*** Description + +This is a long-standing problem when daylight saving time takes effect. Time is +manually set on a KoReader device (or at least, always saved in local time). So +whatever time KoReader reports, we need to know, given the date and the user +profile's historic timezone, how many hours to adjust the KoReader time to get +to GMT to save it in the database. + +** DONE [#A] Fix book scrobbles where page_data is a list :bug:books:scrobbles: +:PROPERTIES: +:ID: 35b323fa-ccc0-4009-b227-8a0f12bbd469 +:END: + +*** Description + +Comic scrobbling is currently kind of janky. Most of the problems boil down to them +storing saved page data in a list of dicts rather than a dict keyed off of the page number that +was read. + +We need to adjust comic scrobbling to use a dict of pages keyed off the page number, and also +write a migration script that runs as a data migration to update any book scrobbles that may +have page_data as a list. + +*** Example data +#+begin_src python +{"notes": null, "page_end": 27, "page_data": [{"notes": null, "end_ts": 1771815895, "duration": 14, "start_ts": 1771815881, "description": null, "page_number": "1"}, {"notes": null, "end_ts": 1771815908, "duration": 13, "start_ts": 1771815895, "description": null, "page_number": "1"}, {"notes": null, "end_ts": 1771815913, "duration": 5, "start_ts": 1771815908, "description": null, "page_number": "2"}, {"notes": null, "end_ts": 1771815933, "duration": 20, "start_ts": 1771815913, "description": null, "page_number": "3"}, {"notes": null, "end_ts": 1771815945, "duration": 12, "start_ts": 1771815933, "description": null, "page_number": "4"}, {"notes": null, "end_ts": 1771815983, "duration": 38, "start_ts": 1771815945, "description": null, "page_number": "5"}, {"notes": null, "end_ts": 1771816007, "duration": 24, "start_ts": 1771815983, "description": null, "page_number": "6"}, {"notes": null, "end_ts": 1771816011, "duration": 4, "start_ts": 1771816007, "description": null, "page_number": "7"}, {"notes": null, "end_ts": 1771816013, "duration": 2, "start_ts": 1771816011, "description": null, "page_number": "8"}, {"notes": null, "end_ts": 1771816052, "duration": 39, "start_ts": 1771816013, "description": null, "page_number": "7"}, {"notes": null, "end_ts": 1771816127, "duration": 75, "start_ts": 1771816052, "description": null, "page_number": "8"}, {"notes": null, "end_ts": 1771816134, "duration": 7, "start_ts": 1771816127, "description": null, "page_number": "9"}, {"notes": null, "end_ts": 1771816196, "duration": 62, "start_ts": 1771816134, "description": null, "page_number": "10"}, {"notes": null, "end_ts": 1771816262, "duration": 66, "start_ts": 1771816196, "description": null, "page_number": "11"}, {"notes": null, "end_ts": 1771816293, "duration": 31, "start_ts": 1771816262, "description": null, "page_number": "12"}, {"notes": null, "end_ts": 1771816322, "duration": 29, "start_ts": 1771816293, "description": null, "page_number": "13"}, {"notes": null, "end_ts": 1771816330, "duration": 8, "start_ts": 1771816322, "description": null, "page_number": "14"}, {"notes": null, "end_ts": 1771816368, "duration": 38, "start_ts": 1771816330, "description": null, "page_number": "15"}, {"notes": null, "end_ts": 1771816388, "duration": 20, "start_ts": 1771816368, "description": null, "page_number": "16"}, {"notes": null, "end_ts": 1771816482, "duration": 94, "start_ts": 1771816388, "description": null, "page_number": "17"}, {"notes": null, "end_ts": 1771816550, "duration": 68, "start_ts": 1771816482, "description": null, "page_number": "18"}, {"notes": null, "end_ts": 1771816567, "duration": 17, "start_ts": 1771816550, "description": null, "page_number": "19"}, {"notes": null, "end_ts": 1771816586, "duration": 19, "start_ts": 1771816567, "description": null, "page_number": "20"}, {"notes": null, "end_ts": 1771816597, "duration": 11, "start_ts": 1771816586, "description": null, "page_number": "21"}, {"notes": null, "end_ts": 1771816616, "duration": 19, "start_ts": 1771816597, "description": null, "page_number": "22"}, {"notes": null, "end_ts": 1771816640, "duration": 24, "start_ts": 1771816616, "description": null, "page_number": "23"}, {"notes": null, "end_ts": 1771816690, "duration": 50, "start_ts": 1771816640, "description": null, "page_number": "24"}, {"notes": null, "end_ts": 1771816702, "duration": 12, "start_ts": 1771816690, "description": null, "page_number": "25"}, {"notes": null, "end_ts": 1771816823, "duration": 121, "start_ts": 1771816702, "description": null, "page_number": "26"}, {"notes": null, "end_ts": null, "duration": null, "start_ts": 1771816823, "description": null, "page_number": "27"}], "page_start": 1, "pages_read": 27, "resume_url": null, "description": null, "koreader_hash": null, "long_play_complete": false} + +#+end_src +** DONE [#A] Lichess imports do not set default visbility :boardgames:bug:importers:lichess: +:PROPERTIES: +:ID: a78f7c72-a20a-8db2-cde0-d92a731d4fba +:END: + +* Version 50.2 [2/2] +** DONE [#B] Koreader imports only import single-page scrobbles the next day :bug:books:importers: +:PROPERTIES: +:ID: b50141fd-cda6-4a3a-afd3-cd8499e7523e +:END: + +*** Description + +When you read a single page in a book in Koreader and try to import it, the scrobble is only +created the day after, not on the day of the reading. + +** DONE [#A] Fix bugs in celery tasks causing imports to fail :bug:celery:tasks: +:PROPERTIES: +:ID: d1171cb0-6413-44b8-a68a-019a4d2fb285 +:END: + +*** Description + +Seems like all celery tasks are failing for different reasons except the chart +updates. + +*** Errors +**** scrobbles.tasks.send_notification_for_in_progress +#+begin_src bash +KeyError: 'track' + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): +File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute +return self.cursor.execute(sql, params) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +psycopg2.errors.UndefinedColumn: column music_track.artist_id does not exist +LINE 1: ..."."title", "music_track"."base_run_time_seconds", "music_tra... +^ +HINT: Perhaps you meant to reference the column "music_track.artist_fk_id". +#+end_src + +**** scrobbles.tasks.import_from_webdav_all_users +#+begin_src bash +File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/importers/webdav.py", line 166, in scan_webdav_for_koreader +if last_import and last_import.webdav_etag and remote_etag: +^^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'KoReaderImport' object has no attribute 'webdav_etag' +#+end_src + +**** scrobbles.tasks.process_bgstats_import +#+begin_src bash + +Traceback (most recent call last): +File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute +return self.cursor.execute(sql, params) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +psycopg2.errors.NotNullViolation: null value in column "visibility" of relation "scrobbles_scrobble" violates not-null constraint +DETAIL: Failing row contains (374463, 2026-06-11 13:27:06.528319+00, 2026-06-11 13:27:06.52834+00, 2026-06-11 13:17:34+00, 180, f, f, BG Stats, 1, null, t, {"players": [{"new": false, "win": false, "rank": 0, "role": "",..., null, null, null, 8e73ceec-b731-4623-9637-712bbf9f76ce, null, null, null, null, , null, BoardGame, 324, null, null, America/New_York, null, null, , null, null, , null, null, null, null, null, null, null, null, null, null, null). +#+end_src + +* Version 50.1 [1/1] +** DONE [#B] Fix bug in charts where only #1 is displayed :charts:templates: +:PROPERTIES: +:ID: 7136dffb-e6b7-184b-48ac-bb09bae0b0f0 +:END: + +* Version 50.0 [2/2] +** DONE [#A] Allow updating all a user's scrobble visibility at once :scrobbles:sharing:feature: +:PROPERTIES: +:ID: 9ed2ec65-bf69-4300-965c-6a7d3ef7ea03 +:END: + +*** Description + +We now have the ability to share or unshare scrobbles and create private links. +We should add a toggle in the user's settings that will bulk make all their +scrobbles public or private, so that a user can either share everything, or lock +their account down. + +This should not affect scrobbles that are in the "Shared" visibility state. + +And users should be able to also control whether all scrobbles of a specific +type are shared or not. Maybe this could be a JSONField in profile that contains +a media_type key with a visibility type for a value, and if it's not present, +sharing defaults to private? + +Additionally, users's should have links in their settings to see what scrobbles +are either public, shared or private. Probably this could be done with a +?visbility=<> filter on the /scrobbles/ page. + +*** Changes + +- Added `media_type_visibility` JSONField to UserProfile (migration 0038) +- Created `BulkVisibilityView` at `/settings/visibility/` with: + - Radio toggle to make all non-shared scrobbles Public or Private + - Per-media-type dropdown for each of the 20 media types (inherit/public/shared/private) +- Created `BulkVisibilityForm` with dynamic media_type fields +- Created `profiles/visibility_settings.html` template with visibility stats + filter links +- Added link from main settings page to visibility settings +- Added `?visibility=` filter support to `ScrobbleListView` (public/shared/private) +- Added filter indicator to `scrobble_all_list.html` +- Updated `Scrobble.create()` to check `user.profile.media_type_visibility` for media-type-specific defaults before falling back to PRIVATE + + + +** DONE [#A] Replace columsn of Top Artists, Tracks and Series with Maloja widget :templates:charts: +:PROPERTIES: +:ID: 3946afb1-932c-46fe-a188-f4c9add1a491 +:END: + + +*** Description + +The tables are fine, but Maloja widgets are better. We should drop the top track table, add top albums +and replace top artists and top tv series with the Maloja style widgets. + + +* Version 49.1 [1/1] +** DONE [#A] Fix bug with missing default visbility for scrobbles :bug:scrobbles:sharing: +:PROPERTIES: +:ID: 20843992-6453-9a9a-cde6-2c2b6677db23 +:END: + +*** Description + +We can't scrobble anything now because visbility is not null, but has no default +value. + +*** Notes +- Note taken on [2026-06-09 Tue 13:14] + The full stack trace: + #+begin_src sh + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/models.py", line 1430, in create_or_update + elif "log" in scrobble_data.keys() and scrobble.log: + ^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/models.py", line 1583, in create + ) + + File "/usr/local/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method + return getattr(self.get_queryset(), name)(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django/db/models/query.py", line 658, in create + obj.save(force_insert=True, using=self.db) + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/models.py", line 870, in save + if self.media_obj: + ^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django_extensions/db/models.py", line 22, in save + super().save(**kwargs) + File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 814, in save + self.save_base( + File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 877, in save_base + updated = self._save_table( + ^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 1020, in _save_table + results = self._do_insert( + ^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 1061, in _do_insert + return manager._insert( + ^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method + return getattr(self.get_queryset(), name)(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django/db/models/query.py", line 1805, in _insert + return query.get_compiler(using=using).execute_sql(returning_fields) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1822, in execute_sql + cursor.execute(sql, params) + File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 67, in execute + return self._execute_with_wrappers( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers + return executor(sql, params, many, context) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute + with self.db.wrap_database_errors: + File "/usr/local/lib/python3.11/site-packages/django/db/utils.py", line 91, in __exit__ + raise dj_exc_value.with_traceback(traceback) from exc_value + File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute + return self.cursor.execute(sql, params) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + django.db.utils.IntegrityError: null value in column "visibility" of relation "scrobbles_scrobble" violates not-null constraint + DETAIL: Failing row contains (373813, 2026-06-09 17:13:38.11355+00, 2026-06-09 17:13:38.113566+00, 2026-06-09 17:13:36+00, 0, f, f, Todoist, 1, null, t, {"title": "Animal chores", "labels": ["chore", "farm"], "todoist..., null, null, null, 68680dbf-f9a9-476c-b1c7-adbd231bbab6, null, null, null, null, , null, Task, null, null, null, America/New_York, null, null, , null, null, , 72, null, null, null, null, null, null, null, null, null, null). + #+end_src + +* Version 49.0 [1/1] +** DONE [#A] Fix broken tests with new sharing and add tests :scrobbles:sharing:tests: +:PROPERTIES: +:ID: 10ecd169-eaee-8554-d4ee-f1d34bfad99f +:END: + +* Version 48.3 [1/1] +** DONE [#A] Fix bug in missing sqids dep :dependencies:project: +:PROPERTIES: +:ID: 0b619837-729a-cd74-7a97-6fa2a148b27d +:END: + +* Version 48.2 [1/1] +** DONE [#A] Lock down scrobbles and use sqids to share them :feature:sharing:scrobbles: +:PROPERTIES: +:ID: a6e869f7-8012-7e83-8f68-d0a0ed4c3c6a +:END: + +*** Description + +Currently all scrobbles are public. Anyone with the uuid can view any other +scrobbles. We should use SQIDs to allow shareable links to scrobbles and then +make all scrobbles hidden by default. + + +* Version 48.1 [2/2] +** DONE [#A] Generate a report of tracks with mistmatched metadata :music:tracks:metadata: +:PROPERTIES: +:ID: 684b8cd2-a3c1-4995-ba9e-7abdb02c37f2 +:END: + +*** Description + +We should have a management command that outputs a CSV file of track IDs where +the log["raw_data"]["Artist"] (for Jellyfin) or log["raw_data"]["artist"] +(mopidy) value does not match the Track.artists names. + +And we should see the same thing for albums (log["raw_data"]["Album"] or +log["raw_data]["album"]). + +It should output the fields "track_id", "track_artist_name", "track_album_name", +"raw_artist", "raw_album", "source" + +Where source is either Jellyfin or Mopidy based on the keys. + +Put the file /tmp/metadata-report.csv by default and overwrite exsiting reports. + +The command should also accept a file-path to overide this default. + + +** DONE [#A] Date parsing failing in eBird imports :birds:ebird:importers: +:PROPERTIES: +:ID: 72e0254f-39d8-4843-9857-623e0362d77e +:END: + +*** Description + +On line 45 in the apps/birds/importer.py file, the import is thorowing this +error: + +#+begin_src python +ValueError: time data 'Jun 7, 2026, 5:15 PM' does not match format '%B %d, %Y %I:%M %p' +#+end_src + +Historically other files starting on May 24 worked, so I suspect this is a +problem of the date formatter expecting Long month names and zero-padded days +and the only sample we had was a three-letter month (May) and days with two +digites(24 through the 31) + +We should also add a "error_log" to the importers so that errors tha occur are +surfaced, even when 0 successful files were processed. And we should make sure +all importers do this as well. + + + +* Version 48.0 [2/2] +** DONE [#B] Show team or player images on sport detail and scrobble detail :sports:templates: +:PROPERTIES: +:ID: 68c17383-ee6e-4b5f-b3f5-1b637a0a3ea8 +:END: + +*** Description + +On the sport event detail page, we should show the images of the teams or +players invovled. + +Also, those images for the sport event should be shown on the scrobble detail +page for sport event scrobble details. + +** DONE [#B] Add fix_metadta method to Video instances :videos:metadata: +:PROPERTIES: +:ID: 9df5404d-1b60-4eee-b7cf-1f7e6dfade65 +:END: + +*** Description + +Turns out we don't have a fix_metadata method for videos. We should add that using +the basic logic from find_or_create on the Video model. + + +* Version 47.2 [1/1] +** DONE [#B] Add OMDB source as backup when TMDB returns nothing :videos:metadata:imdb: +:PROPERTIES: +:ID: 20195445-7fdd-49be-9767-103b12da0bfb +:END: + +*** Description + +TMDb works great for most cases. There are some edge cases, though where it does +not import videos, when TV shows are split up differently in TMDb than in IMDB. +One example I stumbled on is the 2020 reboot of Animaniacs. TMDb splits the +epiodes up in three parts, while they were always broadcast three-in-one, and +that's how IMDB lists them. Thus, the IMDB ID means nothing, and the videos end +up unenriched. + + +* Version 47.1 [1/1] +** DONE [#A] Untangle the sports migrations errors :sports:bug:migrations: +:PROPERTIES: +:ID: 4d50ca2e-f45b-dde8-e3c9-cd84f353b349 +:END: + + +* Version 47.0 [1/1] +** DONE [#B] Change sports scrobbling a bit :feature:sports:scrobbles: +:PROPERTIES: +:ID: cd27d683-c847-4251-b3d1-8243f45c01ca +:END: + +*** Description + +Currently, the way we scrobble sports means that basically the same event will +never be scrobbled again. I will likely never watch the 2025 Monaco Grand Prix +again, but I will watch the Monaco Grand Prix again. But I also wont watch one +specific game between Arsenal and Man City twice, but I may watch those two +teams play multiple times. + +What if instead of scrobbling a specific sports event on a specific date, we +make the unique Scrobblable item the players or teams in the event? + +That would not work for races where the unique item would have to be the name of +the event. + +Maybe that means SportEvent is too generic, and we'd need the event type to be +scrobble items. + +A race, the Indy 500 or Coke 600, or Boston Marathon would be scrobblable, while +for games, the teams would be unique, so a game between Arsenal and Man City +would be unique (with extra logdata context for who's home and who's away, and +the location, could even have the weather per scrobble). + +And finally, for Tennis, the title would be the round of the event, Roland +Garros Women's Semifinal, US Open Men's Final, Miami Invitational Round of 32, +with two players, or two teams and a start datetime, which is similar to what we +have now. The round becomes not a foreign key, but just a string, and we'd need +a FK to an organizer field which would replace league, and would be like "ATP +Tour" or "PGA Tour". Season would also need to be a string, and would be +something like: 2026 or 2024-2025. + +Examples: + - Super Bowl + - Sochaux v Concarneau + - French Open Final + - Carlos Alcaraz v Jannik Sinner + +We'd also want a script to reorganize existing sports events and move scrobbles +to the right place as best as we're able, and to flag sportsevents and scrobbles +that could not automatically be migrated with a unique tag like +"migration-failed" + +Ultimately I think what we need is to greatly simplify the SportEvent to be just a placeholder +for a sport event type for a given league, then each scrobble holds the details of teams, players +start, thesportsdb_id, round and season. + +Thus, I've already simplified that model, but what we need is a migration script that will move +existing complex SportEvent instances into very basic ones, and updating any scrobbles for those +events with a new SportEventLogData structure with all the specific details in it. We also need to +move the obj.round.season.league into the FK for the given event. + + + +* Version 46.0 [1/1] +** DONE [#C] Add sentiment parsing for Scrobbles with notes :scrobbles:sentiment: +:PROPERTIES: +:ID: 37781d6a-f3b0-48b2-bf98-33c2c791cf85 +:END: + +*** Description + +Not sure how useful this would be, but I wonder if we can add a `sentiment` JSONField +on each scrobble that can store the output of VADER over the notes in a scrobble with notes. + +I'm not sure that the value prop here is worth the storage and processing time. + +But if we do add it, it should be a process that scans for scrobbles with both notes and no +sentiment field value (unless --overwrite is used) and just run periodically. + + +* Version 45.1 [1/1] +** DONE [#B] Mopidy favorites or monthly playlist adds should look at all scrobbles :bug:mopidy:favorites:tracks: +:PROPERTIES: +:ID: 0be7d11e-e268-2fd5-836a-e5b4d210e0fa +:END: + +*** Description + +When favoriting a track and trying to add it to the Moidy favorite playlist, it +sometimes happens that one scrobble did not come from Mopidy, but an earlier or +later one did. + +Can we scan all the scrobbles of the track for a given user to see if any have +`mopidy_uri` in the log and if so, use that to send along to Mopidy? + + +* Version 45.0 [1/1] +** DONE [#B] Add ability to add mopidy tracks to Monthly playlists :feature:favorites:tracks: +:PROPERTIES: +:ID: c872ff0a-e71f-415f-b5a6-e62ea9634d14 +:END: + +*** Description + +Now that we can favorite a mopidy track and have it added to a Favorites playlist, it would +be great if we could also populate a monthly_mopidy_playlist_pattern in a user profile and, +if configured, you could press "Add to monthly playlist" button on a given track that has +a mopidy_uri in it's log, and it would be added to the playlist. + +The patterns would be based on traditional Django date formatting patterns: https://gregbrown.co/code/date-format + +So "Y m F" would yield "2026 05 May" if the link is clicked in May of 2026. + + +* Version 44.0 [1/1] +** DONE [#B] Add favorite feature for scrobbles :feature:favorites:scrobbles: +:PROPERTIES: +:ID: 2780ae5f-fe23-49a5-8b33-d19e7f3e8ec6 +:END: + +*** Description + +Would be great to have a FavoriteMedia data model that would accept any media_type ID and a user_id +marking that media as a favorite for that user. + +Additionally, for tracks, we should add the ability to set a "favorites_mopidy_playlist" in a user profile +and if populated, and a track media type is favorited, and the track has a mopidy_uri value in a scrobble log, +send a POST to the mopidy server RPC endpoint for the favorite playlist and add the track. + + + +* Version 43.0 [5/5] +** DONE [#B] Can we show a graph of all past Weigh-in tasks :scale:tasks:graphs:javascript: +:PROPERTIES: +:ID: ae499d87-03bf-4e48-9b2c-1a421a46af11 +:END: + +*** Description + +I wonder if, as a special type of task, Weigh-in's could show a graph of the +metrics that are stored against all the past weigh-ins? + +The graph would contain all Weigh-in scrobbles for that user, no matter which +date is being viewed, and the highlighted value on the graph would be the date +being viewed. + +Probably could use something like chart.js although maybe that's too heavy? + +And can we have each metric overlayed on the same graph? + +** DONE [#B] When viewing scrobbles by tag, sum the total time :scrobbles:tags: +:PROPERTIES: +:ID: d51f23df-c2c5-4e1a-b000-67c89032af02 +:END: + +*** Description + +On scrobbles filtered by tags, we should see a sum of the time spent doing those tasks, in a human +readable format like "X days, X hours, X minutes and X seconds" + +** DONE [#A] Orgmode tasks are not updated if in progress :tasks:orgmode:bug: +:PROPERTIES: +:ID: 7dcebb2c-7c4c-4ac5-bee6-c2e36c3811f9 +:END: + +*** Description + +Currently if you POST to the orgmode webhook with a task that's already in progress, the request just stops there. + +We should add logic where if the task is in-progress, instead of doing nothing, +it checks the webhook payload against the in-progress tasks and updates the +description of the scrobble.log with the incoming task description if it's +different. And the same for comments. If a comment (by timestamp key) is +different in the webhook than what's in the scrobble.log, update the comment in +the scrobble.log + +** DONE [#A] Ignore tag 'inprogress' for Tasks :bug:tasks:tags: +:PROPERTIES: +:ID: cd37c1ec-e2fc-b93c-daf8-6b329712c3f1 +:END: + +*** Description + +When scrobbling tasks from Todoist, the tag `inprogress` is always in the +payload, because that's how we parse tasks starting from the Todoist webhooks. + +But we don't really need anything tagged as `inprogress` Can we ignore this tag +when applying tags to Task scrobbles coming from Todoist?` + + + +** DONE [#A] Deploys are now throwing an unknown version error :bug:tooling:releases: +:PROPERTIES: +:ID: 3870f9d3-b5ed-4b87-9e8c-9bf905bfb766 +:END: + +*** Description + +Almost everything is working, but for some reason `__version__` does not seem to +exist. + +#+begin_src sh +out: Installing collected packages: vrobbler +out: Successfully installed vrobbler-42.0 +err: WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv +err: Traceback (most recent call last): +err: File "", line 1, in +err: AttributeError: module 'vrobbler' has no attribute '__version__' +2026/06/04 17:18:15 Process exited with status 1 +failed to remove container: Error response from daemon: removal of container c8ac64bee9b6bf5978d2c16f299e5ac271d8bbf7192b7a4023c3712bc2444f8b is already in progress + ❌ Failure - Main Install wheel and restart services +exit with `FAILURE`: 1 +#+end_src + + +* Version 42.0 [1/1] +** DONE [#B] Add ability to add track to current Mopidy queue :feature:mopidy:tracks: +:PROPERTIES: +:ID: 79d5b580-4ea6-461b-4c6c-2c950d8b3e4c +:END: + + +* Version 41.0 [5/5] +** DONE [#B] For any scrobble detail page with notes display them better :templates:notes:scrobbles: +:PROPERTIES: +:ID: c0dcf9da-227f-4a22-bcd9-9d46053607d9 +:END: + +*** Description + +Currently notes are displayed as little post-it notes. This is cute, but not terribly useful. + +We should update note rendering to be a simple newest to oldest display in a +single column with the timestamp has a small header, and the content rendered as +markdown with a small bar or horizontal divider marking them from the next note. + +** DONE [#A] Imports should send notifications :feature:notifications:imports: +:PROPERTIES: +:ID: 6f78f8d5-ecaa-4d8a-a666-ae4e27653191 +:END: + +*** Description + +Currently importing board games sends out a ntfy message when a scrobble is +created. + +We should do the same thing for other import types; namely: gpx, ebird, and scale. + +** DONE [#A] Board game imports send duplicate ntfy message :bug:notifications:boardgames: +:PROPERTIES: +:ID: 8f067432-0399-4b79-9e93-727edcccedbd +:END: + +*** Description + +When a board game scrobble is created via a bgstats import, ntfy messages are +sent. + +But right now they are duplicated (two are sent at the same time). Can we review +the code to see why this is happening and fix it? + +** DONE [#A] Too many geolocation notifications go out :bug:notifications:geolocations: +:PROPERTIES: +:ID: 6357ad7a-fe4e-49dd-a063-55d87e459c17 +:END: + +*** Description + +Currently ntfy gets overwhelemed when there's more than a hundred or so messages left in a queue on a client. + +It would be nice if we could not spam ntfy, and this is especially true with Geolocations, where we really +don't need to alert folks unless they have a named Geolocation (has a title). Can we adjust the ntfy +sending for Geolocations to only send if the scrobbled location has a title? + + +** DONE [#C] Fix bug where Weigh-in imports do not set title :bug:tasks:scale: +:PROPERTIES: +:ID: 622e354a-8e66-4ecd-9e1c-a53f0a2ec362 +:END: + +*** Description + +Currently when we import a scale CSV row and create data, the title is left +blank which makes it look funny in a list view. Let's save the weight as the +title of the Weigh-in task. + + +* Version 40.2 [1/1] +** DONE [#A] Try fixing deploy bugs again :tooling:releases:bug: +:PROPERTIES: +:ID: 15894943-be1d-200f-8400-a136770ad9d2 +:END: + + +* Version 40.1 [2/2] +** DONE [#A] Releases are still broken :bug:releases:tooling: +:PROPERTIES: +:ID: bca37a18-afa2-4ddd-a11b-ef0555f38bc9 +:END: + +*** Description + +Deploys are still broken, even with them being pulled apart and run separately. + +We need to address the way the commit ends up stashed in the codebase. + +** DONE [#C] Fix bug on chart pages where trail titles missing :bug:trails:charts: +:PROPERTIES: +:ID: 21075430-8a93-4e59-9a02-479315960ae6 +:END: + +*** Description + +When trails are rendered on the chart views, there are no titles, which means we don't see +anything except the ranking number. + + +* Version 40.0 [2/2] +** DONE [#A] Fix error in org-mode task sync :emacs:orgmode:tasks:bug: +:PROPERTIES: +:ID: 03256d2a-48aa-4be7-aeb3-fa1cfddc86bf +:END: + +*** Description +#+begin_src python + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/tasks/webhooks.py", line 236, in post + emacs_scrobble_update_task( + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/scrobblers.py", line 844, in emacs_scrobble_update_task + for note in emacs_notes: +TypeError: 'NoneType' object is not iterable +#+end_src +** DONE [#B] Adjust how similar artists are shown :feature:templates:artists:music: +:PROPERTIES: +:ID: 2a081620-a0a2-4851-a7cf-4043f9c2ee31 +:END: + +*** Description + +Currently we show the top 10 similar artists on the Artist detail page linked to +the artist detail page on Vrobbler. + +First off, this is very slow. We should look into speeding up the rendering of +the similar artists widget. + +Second, the artist name in the similar artist list should be a link to the +Vrobbler artist detail page, but there should also be a [musicbrainz] link next +to it, that links out to the musicbrainz page whether we have the artist in the +Vrobbler database or not. + + + +* Version 39.3 [2/2] +** DONE [#A] Issue found when doing a release :bug:tooling:release:cicd: +:PROPERTIES: +:ID: a8fc3ec9-74ec-4190-8ac2-62cd8a33e828 +:END: + +*** Description +#+begin_src sh +err: ERROR: Cannot install vrobbler 0.16.1 (from /var/lib/vrobbler/dist/vrobbler-0.16.1-py3-none-any.whl) and vrobbler 38.0 (from /var/lib/vrobbler/dist/vrobbler-38.0-py3-none-any.whl) because these package versions have conflicting dependencies. +out: The conflict is caused by: +out: The user requested vrobbler 0.16.1 (from /var/lib/vrobbler/dist/vrobbler-0.16.1-py3-none-any.whl) +out: The user requested vrobbler 38.0 (from /var/lib/vrobbler/dist/vrobbler-38.0-py3-none-any.whl) +out: To fix this you could try to: +out: 1. loosen the range of package versions you've specified +out: 2. remove package versions to allow pip attempt to solve the dependency conflict +err: ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/latest/topics/dependency-resolution/#dealing-with-dependency-conflicts +2026/06/01 14:15:00 Process exited with status 1 +failed to remove container: Error response from daemon: removal of container 3fe0eaf032c5518aca4ab71734b52bda7c54ed406136b82136ab7155bf5ff3c1 is already in progress +#+end_src + +** DONE [#A] Fix deploy actions running twice :bug:tooling:cicd: +:PROPERTIES: +:ID: aa56f2a6-2b61-4ddf-9e27-9eadcddf8412 +:END: + +*** Description + +Turns out we're now running the build-deploy action twice, once on branch push +and once on tag push. + +We should do builds on each push and build and deploys only when a new tag is +detected. + + +* Version 39.2 [2/2] +** DONE [#B] Releases do not pin commit to the repo for display :bug:tooling:releases: +:PROPERTIES: +:ID: 2a9f2ff5-2642-47ab-ba1d-e41825411713 +:END: + +*** Description + +Somewhere in implementing the justfile release flow, we lost the capture of the +latest commit in the relesae flow so the footer now always says: vXX.x (unknown) + +It should have the first bit of the commit in the parens at the end. + +** DONE [#B] Fix the way timestamps are stored for notes on tasks :bug:scrobbles:tasks: +:PROPERTIES: +:ID: 32973bb3-079b-8cdf-6495-82f8ae907299 +:END: + +*** Description +:PROPERTIES: +:ID: d833a3df-6eb2-36bb-1863-e438e5d36151 +:END: + +Turns out Todoist uses a human-readable timestamp format for comments. We should +adapt that for use from org-mode as well. + +Format should be: "%Y-%m-%dT%H:%M:%S.%fZ" + + +* Version 39.1 [1/1] +** DONE [#A] Fix bug in tests for notes saving :bug:scrobbles:forms: +:PROPERTIES: +:ID: 68a011b2-bb6f-3ba8-2312-5947c41db9ac +:END: + + +* Version 39.0 [3/3] +** DONE [#B] Clean up org-mode tasks metadata :bug:tasks:metadata: +:PROPERTIES: +:ID: 0c762d09-fc69-4e75-be40-7eaaf04f178e +:END: + +*** Description + +Org-mode tasks have a `Description` subheader, which should populate the +"Description" of a task. + +The title should come from the actual TODO content, with tags coming from the +tags in colons after the task. This behaviour should already work. + +Next, any comments should go under a `Comments` subheader. Under these should be a list tag like: + +"Note taken on [2026-05-31 Sun 20:03]" + +Which declares it's a note with a timestamp like "YYYY-MM-DD d HH:MM". + +When scrobbling a task from org-mode, the description should always be copied to +the scrobble's log["description"]. + +Any comments that exist on the task when the scrobble is first created, should +be ignored. + +Any comments on a task that is updated (a re-POST'd while the task is in +progress, should add a comment in the log["notes"], a dictionary, keyed off the +timestamp the note string. If a note for that datetime already exists, the +content should be replaced with the new comment. + +Additionally, when a task is re-POST'd while the task is in progress, the +description should also be compared, and if different, the newest description +should be used. + +Note, the biggest change for this flow is making sure comments that already +exist are not added to any new tasks and that both comments and descriptions are +added or updated if the new values are different than what is already in the +currently in-progress task. + +If the task is completed, don't touch it. + +*** Comments +- Note taken on [2026-05-31 Sun 20:03] + +** DONE [#A] Actually push branches up and add a just command to do it :release:justfile:tooling: +:PROPERTIES: +:ID: 50aa5daa-a802-6aa9-38a3-218b7a9d4b34 +:END: +** DONE [#A] Try to fix deploy failing with bad release plan :release:tooling:pyproject: +:PROPERTIES: +:ID: 63dc633c-4382-e6a5-e663-b01871ce86ce +:END: + + +* Version 38.0 [38/38] +** DONE [#A] Fix release flow to be easier to trigger :pyproject:release:tooling: +:PROPERTIES: +:ID: e321f8a3-26ea-b316-8493-294b50c0b516 +:END: + +*** Description + +We should have a command like `just relesae ` that can cut new +releases on command by updating the PROJECT.org file with a release header for +all DONE tasks and creating the appropriate release commits and tags based on +either the new release version type. + +It should also update the pyproject file. + +** DONE [#A] Move imported retroarch lrtl files to processed/ directory on WebDAV :webdav:retroarch:importers: +:PROPERTIES: +:ID: 881b5af6-e96f-4e71-9e66-2d30824e5504 +:END: + + - File: ~vrobbler/apps/scrobbles/importers/webdav.py~ (line 439) + - Same pattern as the GPX importer: after importing a =.csv= file from + WebDAV, move it to =var/retroarch/processed/= with a timestamp appended. + +** DONE [#A] Add listenbrainz support for similar tracks :feature:music:metadata: +:PROPERTIES: +:ID: a3abba14-7603-db51-58e1-1941a47fa35e +:END: +** DONE [#B] Consolidate albums in the same musicbrainz_releasegroup_id :music:albums:metadata: +:PROPERTIES: +:ID: 6b331bdb-1516-bb86-7cb3-8cb9e3de59ce +:END: + +*** Description + +When we look up albums, we should check if one already exists with the same +musicbrainz_releasegroup_id and prefer that one, rather than creating a new +album. + +Also, we should create a data migration to clean up albums with matching +musicbrainz_releasegroup_id fields by consolidating tracks around the first +album found. + +Whether the track was enriched by musicbrainz or not, the track should get +tagged with `musicbrainz_enriched` or `musicbrainz_notfound` + +** DONE [#A] Clean up metadata on music tracks :music:tracks:metadata:musicbrainz: +:PROPERTIES: +:ID: 3a007bd7-b901-43cb-892e-102d39fffc85 +:END: + +*** Description + +There's a over 3K tracks without a musicbrainz_id and almost 30K without a +base_run_time_seconds. We should have a clean up script that can run through the +ones missing musicbrainz_ids and see about getting metadata from musicbrainz and +for the 30K without base_run_time_seconds, check with their musicbrainz_ids to +see if we can look up the track length, and if not, tag the track with +"missing-metadata" tag. + +Also, if the tracks without musicbrainz_ids actually are not in the MB database, +we should tag those with "not-in-musicbrainz" + +And a big part of this work will probably involve checking for "Various Artists" +tracks where the track erroneously got set with a generic artist. In those +cases, we should try just looking up by track title. + +** DONE [#B] Make artists_m2m field source of artist truth for albums :music:bug:albums: +:PROPERTIES: +:ID: 05a24455-0e71-45ef-ac6e-c1b7b843047d +:END: + +*** Description + +Albums have an FK for album_artist, but like artists, the M2M should be the +source of truth. We should migrate all uses of album_artist to an `artist` +property on the Album model and use a data migration to populate artists with +the album_artist value. + +** DONE [#A] Fix various artist album problem with Superwolves (track with multiple artists) :vrobbler:project:music:bug:artists: +:PROPERTIES: +:ID: 590bc038-745f-710b-8272-4d8a3d2efa01 +:END: + +*** Description +We have an issue with tracks where there are two artists, like `Matt Sweeney & +Bonnie "Prince" Billy` + +I think we need to allow creating a single artist with both names, so the Artist +is the full name. That said, the ampersand is usually used to split feature +artsits (I think) so I'm not sure how this would work reliably. + +Also, it's possible musicbrainz does not have dual artist listings. So it's +possible the longer term solution is to allow multiple artists per track the way +we now allow tracks to be on multiple albums. We could deprecate the Artist FK +or at least make it optional, and then require a M2M between Track and Artist. + +Then this one would be a Track by both `Matt Sweeney` and `Bonnie "Prince" +Billy` + +** DONE [#A] Move imported eBird CSV files to processed/ directory on WebDAV :webdav:ebird:importers: +:PROPERTIES: +:ID: 445e1253-d353-4b55-b1d8-39d0a0dcdd34 +:END: + + - File: ~vrobbler/apps/scrobbles/importers/webdav.py~ (line 439) + - Same pattern as the GPX importer: after importing a =.csv= file from + WebDAV, move it to =var/ebird/processed/= with a timestamp appended. + +** DONE [#A] Move imported Board Game CSV files to processed/ directory on WebDAV :webdav:boardgames:importers: +:PROPERTIES: +:ID: a3f8d30c-d2c3-4ee7-b062-f0f16bd9b0b4 +:END: + + - File: ~vrobbler/apps/scrobbles/importers/webdav.py~ (line 496) + - Same pattern as the GPX importer: after importing a =.csv= file from + WebDAV, move it to =var/bgstats/processed/= with a timestamp appended. + +** DONE [#A] Move imported Scale CSV files to processed/ directory on WebDAV :webdav:scale:importers: +:PROPERTIES: +:ID: 1a0de363-d1ea-466e-9966-e24941a6180b +:END: + - File: ~vrobbler/apps/scrobbles/importers/webdav.py~ (line 496) + - Same pattern as the GPX importer: after importing a =.csv= file from + WebDAV, move it to =var/scale/processed/= with a timestamp appended. + +** DONE [#A] Allow special parameter to re-import already processed GPX files :imports:gpx: +:PROPERTIES: +:ID: 166c0809-c11a-4d02-8071-7a69dcb36e64 +:END: + +*** Description + +Now that we stash imported GPX files in the processed/ subdirectory on import, +it would be nice to have a flag like --include-processed on the webdav importer +that would import files both in the root of gpx/ and also in the processed +directory. This would aide testing imports in staging quickly without constantly +moving files back and forth. + +** DONE [#A] Move imported GPX files to processed/ directory on WebDAV :webdav:gpx:importers: +:PROPERTIES: +:ID: db2b02fc-817c-4c39-bd51-13f9b77c7888 +:END: + - File: ~vrobbler/apps/scrobbles/importers/webdav.py~ (line 198) + - After importing a GPX/FIT file from WebDAV, move it to a =processed/= + subdirectory with a timestamp appended. This eliminates the DB lookup for + already-imported filenames — any file present in the top-level directory + is new. Also makes manual re-imports easy (just move a file back). + +** DONE [#A] Add CSS Grid calendar view for scrobbles :vrobbler:personal:project:templates:feature: +:PROPERTIES: +:ID: be915acf-d803-466a-8770-823819ebf2a9 +:END: + +*** Description + +Calendar view at /scrobbles/calendar/ showing select media types (Tasks, +Birding, Food, Trails, VideoGames, Books) in a CSS Grid month layout. + +- Emoji badges per scrobble on each day cell, hover reveals title +- Click emoji to navigate to scrobble detail +- Prev/Next month navigation +- Based on the CodePen CSS Grid calendar pattern: + https://codepen.io/oliviale/pen/QYqybo + +** DONE [#C] Come up with a possible flow using WebDAV and super-productivity for tasks :personal:feature:project:vrobbler:tasks: +:PROPERTIES: +:ID: dd751edf-6192-2ed0-cd85-905f423f82b0 +:END: +** DONE [#B] Fix PuzzleLogData has no attribute form :vrobbler:puzzles:personal:project:logdata: +:PROPERTIES: +:ID: cb8e697a-9fc2-c463-5d4d-fa15d3e833a0 +:END: +** DONE [#B] Add PuzzleLogData class with with_people and completed :vrobbler:feature:puzzles:logdata:personal:project: +:PROPERTIES: +:ID: 1b38c8a8-86d3-b73d-c584-dfb0497970dc +:END: +** DONE Add weather lookup to the mood check-in flow :vrobbler:project:moods:feature:checkin: +:PROPERTIES: +:ID: 175678e2-bfe1-0833-f2a9-f03b9323db78 +:END: +<2026-05-20 Wed> +** DONE Add importing of openScale CSV files to Tasks :vrobbler:project:personal:tasks:openscale: +:PROPERTIES: +:ID: 344d1f5d-dda6-a1fb-da53-e00eebdd2262 +:END: +** DONE Add ability to track Birding sessions via BirdingLocation scrobbles :vrobbler:project:birds:feature: +:PROPERTIES: +:ID: 8200ce29-a691-5cf6-c11a-c77e3d8b64c6 +:END: +** DONE List only the last 20 scrobbles per category on the home page :vrobbler:project:scrobbles:templates: +:PROPERTIES: +:ID: b56402b9-5542-a10c-6c13-e12f56f2a2d1 +:END: +** DONE Fix display of notes so they look like stickies :vrobbler:project:personal:notes:scrobbles: +:PROPERTIES: +:ID: 0ace8814-e96e-fa54-28d1-c57dcb508f1e +:END: +** DONE Add searching to scrobbles :vrobbler:project:personal:search:scrobbles: +:PROPERTIES: +:ID: b4690c76-d741-4320-20e8-c003251fe035 +:END: +** DONE Fix uniqueness of imdb_id messing up youtube videos :vrobbler:project:bug:videos: +:PROPERTIES: +:ID: 610b8c4c-abf1-8630-a4fd-08d9939da9f5 +:END: + +Turns out you can't make imdb_id unique by itself or you never get to create +youtube videos. Rather, we should make imdb_id and youtube_id unique together so +imdb_id can be empty for every youtube_id and vice versa. + +** DONE Fix genearting chart records :vrobbler:bug:personal:project:chartrecords: +:PROPERTIES: +:ID: e749d762-44ac-dd7e-017e-2423016737ff +:END: + +** DONE [#A] Save raw scrobble request data to every scrobble log :vrobbler:personal:feature:scrobbles: +:PROPERTIES: +:ID: b4686db4-1319-399c-9b84-f0b1036c6815 +:END: + +The idea here is that no matter where the data comes from, we should just save +it in the scrobble for posterity, so we can always in some form recover the +original intent of the scrobble. + +It may also allow us to clean up junk scrobbles if the data was just horribly +wrong. + +** DONE [#B] Clean up follow up notifications for if you're still scrobbling :vrobbler:personal:project:beers:boardgames:notifications:feature: +:PROPERTIES: +:ID: 7937545f-2a62-4188-ad49-34f85fad0c25 +:END: + +- Note taken on [2025-09-30 Tue 09:32] + + I added this feature in a very rough way, but now we should add "Action" + headers so that we can either Finish or Cancel the associated scrobble: + + https://docs.ntfy.sh/publish/#send-http-request + +** DONE [#A] Fix lookup of music tracks from Musicbrainz :vrobbler:bug:tracks:music: +:PROPERTIES: +:ID: 2bc1cc0b-6c4e-bc3d-e7c3-47df364df206 +:END: + +Turns out we're not looking up music tracks properly, again. + +** DONE Check opencode about a way to present stats like movies per month :vrobbler:scrobbles:stats:personal:project: +:PROPERTIES: +:ID: eed39a0b-3aa7-dc65-9e03-a58fa401c8c6 +:END: +** DONE Fix bug in Jellyfin audio track playback :vrobbler:personal:project:bug:music:jellyfin: +:PROPERTIES: +:ID: 002bd234-4507-5313-fb59-2d3baa49b847 +:END: + +#+begin_src shell +ERROR django.request:241 log_response Internal Server Error: /webhook/jellyfin/ +Traceback (most recent call last): + File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner + response = get_response(request) + ^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response + response = wrapped_callback(request, *callback_args, **callback_kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django/views/decorators/csrf.py", line 56, in wrapper_view + return view_func(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django/views/decorators/csrf.py", line 56, in wrapper_view + return view_func(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django/views/generic/base.py", line 104, in view + return self.dispatch(request, *args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 509, in dispatch + response = self.handle_exception(exc) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 469, in handle_exception + self.raise_uncaught_exception(exc) + File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception + raise exc + File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 506, in dispatch + response = handler(request, *args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/rest_framework/decorators.py", line 50, in handler + return func(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/views.py", line 494, in jellyfin_webhook + scrobble = jellyfin_scrobble_media(post_data, request.user.id) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/scrobblers.py", line 177, in jellyfin_scrobble_media + log["album_id"] = media_obj.album_id + ~~~^^^^^^^^^^^^ +TypeError: 'tuple' object does not support item assignment +#+end_src +** DONE [#B] Auto calc duration if no playback time seconds present :vrobbler:bug:scrobbles:personal:project: +:PROPERTIES: +:ID: e16228b2-b062-bd00-32e6-b2353e6406e9 +:END: +** DONE Fix bug in video find_or_create :vrobbler:personal:project:bug:videos: +:PROPERTIES: +:ID: 10e611a5-fbcf-7473-25f0-c9bad7b79ffd +:END: +The error: +#+begin_src shell + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/views.py", line 493, in jellyfin_webhook + scrobble = jellyfin_scrobble_media(post_data, request.user.id) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/scrobblers.py", line 149, in jellyfin_scrobble_media + media_obj = Video.find_or_create(imdb_id) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/videos/models.py", line 390, in find_or_create + return cls.get_from_imdb_id(source_id, overwrite) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/videos/models.py", line 374, in get_from_imdb_id + video.tv_series = Series.find_or_create( + ^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/videos/models.py", line 203, in find_or_create + vdict, _, cover, genres = lookup_video_from_imdb( + ^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/videos/sources/imdb.py", line 17, in lookup_video_from_imdb + imdb_result = imdb.get_title(imdb_id) + ^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/cinemagoerng/web.py", line 127, in get_title + data = _scrape(spec=spec, context=context, headers=headers) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/cinemagoerng/web.py", line 120, in _scrape + document = fetch(url, headers=request_headers) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/cinemagoerng/web.py", line 44, in fetch + with urlopen(request) as response: + ^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/urllib/request.py", line 216, in urlopen + return opener.open(url, data, timeout) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/urllib/request.py", line 525, in open + response = meth(req, response) + ^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/urllib/request.py", line 634, in http_response + response = self.parent.error( + ^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/urllib/request.py", line 563, in error + return self._call_chain(*args) + ^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/urllib/request.py", line 496, in _call_chain + result = func(*args) + ^^^^^^^^^^^ + File "/usr/local/lib/python3.11/urllib/request.py", line 643, in http_error_default + raise HTTPError(req.full_url, code, msg, hdrs, fp) +urllib.error.HTTPError: HTTP Error 500: Internal Server Error +#+end_src + +- Note taken on [2026-03-11 Wed 20:53] + + It turns out that every time a TV show was scrobbled were were hitting IMDB. + Woof. + +** DONE Update admin page to be easier to use :vrobbler:djadmin:project:personal: +:PROPERTIES: +:ID: 0779be1f-1ad7-ddc7-8861-d3c7c833cbe9 +:END: +** DONE Fix migrations and update repo :vrobbler:scrobbles:admin:personal:project: +:PROPERTIES: +:ID: ccbc2ef0-68bf-9ef7-dcaf-febab5ebefe2 +:END: +** DONE Add recipe parsing for food lookups :vrobbler:foods:project:feature:personal: +:PROPERTIES: +:ID: 86456c78-247b-fb63-7ae8-d6d17e7666b1 +:END: +** DONE [#A] Videos are scrobbling duplicates again :vrobbler:bug:videos:scrobbles: +:PROPERTIES: +:ID: a46884fe-7ef1-410a-316b-7ac6d7599331 +:END: +<2026-03-06 Fri> +** DONE Fix board games not saving BGG id on lookup :vrobbler:bug:boardgames: +:PROPERTIES: +:ID: 506c2965-51d6-6cb9-fc4f-4f0468d2d62f +:END: +** DONE Fix board game lookup with name like Unmatched Game System :vrobbler:bug:boardgames: +:PROPERTIES: +:ID: 57956723-b34a-37ad-fde9-44947cc9bb65 +:END: +** DONE [#A] Fix raw text webpage title not truncating to 254 chars :vrobbler:personal:bug:webpages: +:PROPERTIES: +:ID: 13fa0efd-2c3f-dd07-deb2-62882096feff +:END: + +- Note taken on [2025-09-30 Tue 09:33] + + This may have already been resolved ... need to just confirm it. + + +* Version 37.0 [4/4] +** DONE [#A] Tasks from org-mode should properly update notes and leave them out of the body :vrobbler:bug:tasks: +:PROPERTIES: +:ID: c8410001-dbb7-1536-bd89-9784189e058f +:END: +** DONE [#A] Allow scrobbling from the Food list page's start links :vrobbler:bug:food:scrobbling:personal:project: +:PROPERTIES: +:ID: 00f99f60-ac00-6cde-311d-c31f41a01353 +:END: +https://life.lab.unbl.ink/scrobble/e39779c8-62a5-46a6-bdef-fb7662810dc6/start/ +** DONE [#B] Food scrobbles should inherit calories from obj if missing :vrobbler:feature:food:personal:project: +:PROPERTIES: +:ID: 3322ff69-4252-db65-36b3-fae56c1b9327 +:END: +** DONE [#A] Puzzles (and all longplays) should have a "Completed?" column on their detail page :vrobbler:bug:puzzles:personal:project: +:PROPERTIES: +:ID: e3e49a9a-67d2-8ad8-1114-6f05effee9b7 +:END: + +* Version 36.0 [1/1] +** DONE [#A] Refactor how videos are scrobbled :vrobbler:vidoes:feature:personal:project: +:PROPERTIES: +:ID: 6034a11d-5376-994d-9a4b-e1640e258cfa +:END: + +* Version 35.0 [3/3] +** DONE [#B] Add youtube link in place of IMDB on video detail page :vrobbler:feature:videos:personal:project: +:PROPERTIES: +:ID: 84064bd6-2258-a4de-f048-b131db9465c9 +:END: +** DONE [#B] Add missing API lookups to resolve broken scrobbles endpoint :vrobbler:feature:api:scrobbles:personal:project: +:PROPERTIES: +:ID: 0f668a54-f587-3b17-353e-3a56969d3a82 +:END: +** DONE [#A] IMDB lookups are not working :vrobbler:bug:videos:personal:project: +:PROPERTIES: +:ID: d1ba1ca1-509b-13a9-1307-b2dc94a2eafe +:END: + +* Version 34.0 [4/4] +** DONE [#A] Use bgg-api for BoardGameGeek lookups :vrobbler:feature:boardgames:personal:project: +:PROPERTIES: +:ID: 738abb5a-c796-b16b-fe10-6e5639a0e10d +:END: +** DONE [#A] Add classmethod for metadata fetching to tracks :vrobbler:feature:music:personal:project: +:PROPERTIES: +:ID: bc4b45e5-4c65-13c5-ab7b-1937d3fbf5c2 +:END: + +- Note taken on [2025-10-29 Wed 21:44] + + Beyond a classmethod (which I think we have now), we need to update the flow of how we look up tracks. + + It's a hot mess right now where Various Artists walks over the actual artist, and we often hit MB when we don't have to. + +** DONE [#A] Fix views for TV series where next episode is now None :vrobbler:bug:personal:videos: +:PROPERTIES: +:ID: d7014ac4-cda6-0802-2cdf-8f66c6389fea +:END: + +#+begin_src python +ERROR django.request:241 log_response Internal Server Error: /series/c24100d1-da45-4abe-86bf-27cfce9b1f89/ +Traceback (most recent call last): + File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner + response = get_response(request) + ^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response + response = wrapped_callback(request, *callback_args, **callback_kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django/views/generic/base.py", line 104, in view + return self.dispatch(request, *args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django/contrib/auth/mixins.py", line 73, in dispatch + return super().dispatch(request, *args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django/views/generic/base.py", line 143, in dispatch + return handler(request, *args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/django/views/generic/detail.py", line 109, in get + context = self.get_context_data(object=self.object) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/videos/views.py", line 33, in get_context_data + context_data["next_episode_id"] = "tt" + next_episode_id + ~~~~~^~~~~~~~~~~~~~~~~ +TypeError: can only concatenate str (not "NoneType") to str +#+end_src + +** DONE [#A] Emacs tasks are duplicating rather than updating :vrobbler:bug:tasks:emacs:personal:project: +:PROPERTIES: +:ID: e93efc25-7ce9-8ef2-662e-0a19dd0b29c9 +:END: + +- Note taken on [2025-10-29 Wed 16:38] + + Turns out I was misusing `orgmode` for the source of tasks when it shoulda been `Org-mode` + + A good lesson in using constants for things. + + +* Version 33.0 [3/3] +** DONE [#A] Fix bug where scrobble is_stale only uses seconds not total_seconds :vrobbler:bug:scrobbles:personal:project: +:PROPERTIES: +:ID: 7f6070ac-4f67-011d-ebd5-f3dc47da46ed +:END: +** DONE [#B] Fix duplicatged Read next issue for Comic books :vrobbler:bug:books:personal:project: +:PROPERTIES: +:ID: 97943040-1f03-b0b7-b0aa-123a783e4f7b +:END: +** DONE [#A] Add API authentication to BGG calls :vrobbler:bug:boardgames:personal:project: +:PROPERTIES: +:ID: 4955cc34-0882-50db-92f7-f36a95bf57a4 +:END: +<2025-10-28 Tue> + +* Version 32.0 [2/2] +** DONE [#B] Save path to reading source on book scrobbles and show it on the detail page :vrobbler:feature:books:personal:project: +:PROPERTIES: +:ID: f1ef3945-e6e4-66c1-b72e-3cede7a0f84a +:END: +** DONE [#B] Move comic resume URL to next page and check if it exists :vrobbler:feature:books:personal:project: +:PROPERTIES: +:ID: 9fe09567-11a3-7083-53c7-07458a9591d0 +:END: + +* Version 31.0 [3/3] +** DONE [#A] Stop comic book webpage scrobbles from overwriting old scrobbles :vrobbler:personal:bug:books:scrobbling: +:PROPERTIES: +:ID: 4b2ec068-a281-a88b-c31d-6248d6eb0aa0 +:END: +** DONE [#A] Add page calculation to manually scrobbled books :vrobbler:personal:feature:books:scrobbling: +:PROPERTIES: +:ID: b2e313b3-5c35-57e7-8933-627535baf34b +:END: +** DONE [#A] Fix bug in scrobbling comics where google fails :vrobbler:personal:bug:books:scrobbling: +:PROPERTIES: +:ID: 9a870c05-6d20-0803-d35d-c03fbe1d0ee1 +:END: + +* Version 30.0 [3/3] +** DONE [#A] Fix readcomicsonline browsing to update pages :vrobbler:books:feature:comicbook:personal:project:scrobbling: +:PROPERTIES: +:ID: 981b215a-6473-5fc7-d4cc-51b3eddec4c3 +:END: +** DONE [#B] Redirect webpages back to the original page when starting or stopping :vrobbler:project:webpages:bug: +:PROPERTIES: +:ID: 6183d03a-452b-51d5-cceb-5bfeada947aa +:END: + +** DONE [#B] Fix ComicVine as source for comic book metadata :vrobbler:books:feature:comicbook:personal:project:scrobbling: +:PROPERTIES: +:ID: d22cec3f-117f-f203-33a5-efbefa8a5cee +:END: + +* Version 29.0 [1/1] +** DONE HOTFIX podcast lookups, final + +* Version 28.0 [1/1] +** DONE HOTFIX podcast lookups + +* Version 27.0 [3/3] +** DONE [#A] Fix bug where podcast scrobbling creates duplicate Podcast :project:vrobbler:scrobbling:podcasts:bug:personal: +:PROPERTIES: +:ID: 7377ef6c-5fa7-9e4e-9080-f9810a76118c +:END: + +Rather than pick up an existing Podcast using the podcast title in the mopidy +file name, Vrobbler creates a new podcast with no enriched data. Not a big deal +for my use as the volume of podcasts I listen to makes manual fixes easy. But +it's annoying. + +** DONE [#A] Allow reading comic books from readcomicsoline.ru :vrobbler:books:feature:comicbook:personal:project:scrobbling: +:PROPERTIES: +:ID: 7c7e9ecc-b675-68c3-764f-ef771ce5d88f +:END: + +- Note taken on [2025-09-25 Thu 10:52] + + Things to consider are whether we scrobble the issue on one page, send it to + archivebox? (yes), and how best to enrich the data + +** DONE [#A] Add RSS feed lookups to podcasts :vrobbler:personal:feature:podcasts: +:PROPERTIES: +:ID: d60645b0-7578-97c1-0278-05bd9de4269c +:END: + +- Note taken on [2025-10-14 Tue 10:08] + + Turns out the Podcast plugin for mopidy does a pretty good job of showing the + latest file without having to scroll the bottom using only Muse to not parse + the podcast title name. BUT, now we're getting urls like this: + + https://nsf.libsyn.com/rss#77e01251-cb20-4609-b577-d48e985d2e7b + + This is great, because there's more context there, but it has to read out of + the RSS feed. We should add a check in the podcast util to sniff out the file + referenced in the # in that url and populate the info from there. This should + actually be much more reliable than the current state of the podcast lookup + which depends on the file to be name properly. + + +* Version 26.0 [3/3] +** DONE Clean up templates for scrobble details :vrobbler:personal:bug:templates: +:PROPERTIES: +:ID: 43dc1e02-c110-5b49-0ac7-4c4f7656d1aa +:END: +** DONE Add named locations visited to dashboard :vrobbler:personal:feature:locations:templates: +:PROPERTIES: +:ID: ebc365a1-cef4-f75d-569f-c24b072ef5a4 +:END: +** DONE Add moods to dashboard :vrobbler:moods:feature:templates:personal: +:PROPERTIES: +:ID: c03a38ce-b337-f4fa-adba-aee08d4329f5 +:END: + +* Version 25.0 [3/3] +** DONE Add basic food templates and fix urls :food:vrobbler:personal:project:bug:urls: +:PROPERTIES: +:ID: 3de3459e-8e7e-abba-e068-b919a819d3e3 +:END: +** DONE [#C] Fix how elapsed time is calculated :vrobbler:personal:project:scrobbles:bug: +:PROPERTIES: +:ID: cff58fc4-06ac-8016-4eae-130b51e3c9b7 +:END: +** DONE Fix templates for videos and dashboard links :personal:feature:project:vrobbler:templates: +:PROPERTIES: +:ID: 7debfbaf-cdd8-f49b-57ff-804bfe7c9236 +:END: + +* Version 24.0 [2/2] +** DONE Clean up logdata for various media :personal:feature:project:vrobbler:logdata: +:PROPERTIES: +:ID: d5cce807-1f45-ef19-45a4-9f7069fa2a93 +:END: +** DONE Removed sidebar and add links to headers :personal:feature:templates:scrobbles: +:PROPERTIES: +:ID: 1a1c0aa6-0313-c8be-1676-5d6adddef0a4 +:END: + + +* Version 23.0 [3/3] +** DONE Add dynamic forms for LogData classes :personal:feature:vrobbler:project:forms:logdata: +:PROPERTIES: +:ID: 0db889a1-f262-fba2-7fed-ed99eded1c88 +:END: +** DONE Look in comments for a timestamp for start from BG stats if the time is missing :vrobbler:feature:boardgames:project:personal: +** DONE Fix long play scrobbles to provide better data :vrobbler:feature:scrobbles:longplay:personal:project: +:PROPERTIES: +:ID: 99f6bd77-dc8f-6ed1-0321-32a52c944264 +:END: + +* Version 19.0 [1/1] +** DONE Add periodic check for mood :vrobbler:feature:moods:personal:project: +:PROPERTIES: +:ID: 55404488-c69f-0dd5-838e-1d1e15c873eb +:END: + +* Version 18.7 [1/1] +** DONE Use the timezone history log to fix old Scrobbles that fall into those timezone blocks :vrobbler:chore:scrobbles:project:personal: +:PROPERTIES: +:ID: 9d055ac1-584b-20c8-7ad9-9ce36b329dc7 +:END: + +* Version 18.4 [2/2] +** DONE Track timezone changes for profiles :vrobbler:feature:profiles:personal:project: +:PROPERTIES: +:ID: 89ec867f-29fd-82f1-be17-b49dddc30c78 +:END: +[2025-07-11 14:23] +** DONE Only create a LastFM import if there are files to import :vrobbler:feature:lastfm:importers:project:personal: +:PROPERTIES: +:ID: 7a1456af-c5f1-6385-b34f-0be24a6b65b0 +:END: +- Note taken on [2025-07-20 Sun 16:21] + + This thing is kicking my butt. As it stands it works, but the scrobbles are not assigned to the tracks properly. + +* Version 18.3 [1/1] +** DONE Add timezone awarness to IMAP importer :personal:project:vrobbler:feature:importer:imap:timezones: +:PROPERTIES: +:ID: 05837b7c-96aa-6190-3678-e2ae7c7cac75 +:END: + +* Version 18 [4/4] +** DONE Condense tracks of the same title by the same artist with multiple albums :vrobbler:feature:music:project:personal: +:PROPERTIES: +:ID: b39fcec8-59fd-eab0-5809-b8144c7d2708 +:END: +** DONE Import from BG stats a "learning" log field when "Learning to play" is in the comment :vrobbler:feature:boardgames:project:personal: +:PROPERTIES: +:ID: fda59fab-4349-e99e-54c6-9f1392a1c474 +:END: +** DONE [#A] Add email importer for BG stats file uploads :vrobbler:feature:boardgames:personal:project: +:PROPERTIES: +:ID: 116fe738-7966-615c-d195-ccff0337b101 +:END: +#+begin_src json example of a file +{ + "about": "This is a Play file that can be read by Board Game Stats. If you see this text, try to use a share, export or open-in function to open it with Board Game Stats.", + "players": [ + { + "uuid": "31f8b92e-11d8-4162-88b1-fd9c79eea249", + "id": 2, + "name": "Colin", + "isAnonymous": false, + "modificationDate": "2025-07-01 18:10:32", + "metaData": "{\"isNpc\":0}" + }, + { + "uuid": "00074700-cf4e-4ad3-b334-d35805bb0d90", + "id": 4, + "name": "Asa Sewell", + "isAnonymous": false, + "modificationDate": "2025-07-01 18:03:37" + } + ], + "locations": [ + { + "uuid": "14f7389c-767f-4725-9b35-906c407b293c", + "id": 3, + "name": "Timberwyck Farm", + "modificationDate": "2025-07-01 18:03:38" + } + ], + "games": [ + { + "uuid": "043a2851-f201-467a-a60c-0b0a7e9c33d2", + "id": 333, + "name": "Ghost Fightin' Treasure Hunters: Anniversary Edition", + "modificationDate": "2025-07-02 01:37:14", + "cooperative": true, + "highestWins": true, + "noPoints": false, + "usesTeams": false, + "urlThumb": "https://cf.geekdo-images.com/DHA-mcH3zzw_OjfDxOPj1A__thumb/img/UhaIm4KIDIiraUc44QIvSAbMUXI=/fit-in/200x150/filters:strip_icc()/pic8266874.jpg", + "urlImage": "https://cf.geekdo-images.com/DHA-mcH3zzw_OjfDxOPj1A__original/img/2-Lb6nLePhn0I0Hh2j1pOtbO4rg=/0x0/filters:format(jpeg)/pic8266874.jpg", + "bggName": "Ghost Fightin' Treasure Hunters: Anniversary Edition", + "bggYear": 2024, + "bggId": 422668, + "designers": "Brian Yu", + "isBaseGame": 1, + "isExpansion": 0, + "rating": 75, + "minPlayerCount": 2, + "maxPlayerCount": 5, + "minPlayTime": 30, + "maxPlayTime": 0, + "minAge": 8 + } + ], + "plays": [ + { + "uuid": "bae3f29e-5e1e-45d8-b409-47a665c8d5b5", + "modificationDate": "2025-07-02 01:37:59", + "entryDate": "2025-07-02 01:31:38", + "playDate": "2025-07-02 01:31:38", + "usesTeams": false, + "durationMin": 23, + "ignored": false, + "manualWinner": true, + "rounds": 3, + "scoresheet": "{\"bggId\":244711,\"version\":1,\"langCode\":\"en\",\"scoreType\":\"bestTotalWins\",\"groups\":[{\"templateId\":\"1\",\"maxRepeat\":-1,\"repetition\":1,\"hasSubTotal\":false,\"hideSingleGroupLabel\":false,\"isExtra\":false,\"rows\":[{\"templateId\":\"vptrack\",\"label\":\"VP track\",\"repetition\":1,\"repeatable\":false,\"negative\":false,\"isExtra\":false,\"scores\":{}},{\"templateId\":\"objectives\",\"label\":\"Objectives\",\"repetition\":1,\"repeatable\":false,\"negative\":false,\"isExtra\":false,\"scores\":{}},{\"templateId\":\"mastercards\",\"label\":\"Master cards\",\"repetition\":1,\"repeatable\":false,\"negative\":false,\"isExtra\":false,\"scores\":{}}]}]}", + "locationRefId": 3, + "gameRefId": 333, + "board": "", + "scoringSetting": 4, + "metaData": "{\"playUsedGameCopy\":2}", + "playerScores": [ + { + "score": "", + "winner": true, + "newPlayer": true, + "startPlayer": false, + "playerRefId": 4, + "role": "", + "rank": 0, + "seatOrder": 0, + "metaData": "{\"scoreUuid\":\"00074700-cf4e-4ad3-b334-d35805bb0d90\"}" + }, + { + "score": "", + "winner": true, + "newPlayer": true, + "startPlayer": false, + "playerRefId": 2, + "role": "", + "rank": 0, + "seatOrder": 0, + "metaData": "{\"scoreUuid\":\"31f8b92e-11d8-4162-88b1-fd9c79eea249\"}" + } + ], + "expansionPlays": [] + } + ], + "userInfo": { + "meRefId": 2 + } +} + +#+end_src +** DONE [#B] Fix task app to only use one tag for the context a task was done in and allow configurable contexts by user profile :personal:vrobbler:feature:tasks:project: +:PROPERTIES: +:ID: 23f485e3-988c-6198-c79d-91fdf92f001c +:END: + +* Version 17.0 [6/6] +** DONE [#A] Fix bug in new task label lookup for Emacs/Org-mode :vrobbler:bug:tasks: +:PROPERTIES: +:ID: 683fb109-dfc4-85e4-80f0-ea618434f61e +:END: +** DONE [#C] Replace commas in the bandcamp URL for artists with nothing :vrobbler:music:bug:personal: +:PROPERTIES: +:ID: 9b30d67b-91f0-a480-dfaa-5d9dc090e76c +:END: + +- Note taken on [2025-06-16 Mon 09:36] + + This firt appeared with Black Country, New Road, where the RYM slug generator + leaves commas in and ends up sending you to a 404. I suspect this wont be the + first tweak we'll need to this, as the RYM link creator is really just + guessing based on the artist name at the path. + +** DONE [#A] Investigate new source of video metadata :personal:project:video:imdb: +:PROPERTIES: +:ID: df2b486c-1170-5199-c312-9bc87760d962 +:END: + +Cinemagoer broke and I probably should find a more reilable source of video data. + +- Note taken on [2025-06-13 Fri 11:19] + + TMDB is much more reliable, but does require an API key. That's all setup now, + so hopefully this breaking IMDB crap is over. + +** DONE [#A] IMDB video lookups are failing :personal:bug:video:imdb: +:PROPERTIES: +:ID: 38f1081f-37b4-f4f2-79aa-c1e87eca4b69 +:END: +<2025-06-13 Fri> + +- Note taken on [2025-06-13 Fri 08:24] + + Looks like Cinemagoer is broken: https://github.com/cinemagoer/cinemagoer/issues/537 + +** DONE [#A] Emacs is not syncing notes :personal:scrobbling:emacs:bug: +:PROPERTIES: +:ID: c79cd491-b30f-0945-d84b-b8cac7562791 +:END: +<2025-06-12 Thu 9:30> + +Not sure if the problem is in my Emacs hook sending or Vrobbler itself. + +- Note taken on [2025-06-12 Thu 09:47] + + Adding a quick note to check on it + +- Note taken on [2025-06-12 Thu 09:50] + + Ah ha. All the messing about with the source field meant that I was looking + for `emacs` as a source but the hook was initially setting sources to + `orgmode` I think I prefer `orgmode` as the source, so updating it thusly. + + Fixed in `490d60cbbb1f8bf90b5fc47d8685b15bdc1d485b` + +** DONE [#A] Show the description of a task in the string rep for a scrobble of a Task :personal:project:scrobbling:vrobbler:feature: +:PROPERTIES: +:ID: df58f8d0-fa4a-2037-c7d7-e5388c239042 +:END: + +* Version 0.16.0 [19/19] +** DONE [#A] Jellyfin, bandcamp tracks from Mopidy create duplicate music tracks :bug:scrobbling:music: +:PROPERTIES: +:ID: 670e8634-49b5-dce9-1684-14f2ffb797f1 +:END: +Effectively, any track that comes in without a MusicBrainz ID does some funky +lookup where it doesn't find a track without an MB id and the track title / +artist combination and creates a new track every time. This has to be cleaned up +by condensing the duplicated tracks into the original proper track. + +But it opens a bigger question about how much MB id should the drive the app +lookup. If it can't be depended on to exist from all sources, it really can't be +canonical. Instead, the combination of track title / artist is really the best +we can do. Last.fm also has this problem, where it doesn't know about albums and +definitely does not know or care about MB ids. + +** DONE Add a user profile page with ability to change settings :profiles:improvement: +- Note taken on [2025-04-04 Fri 10:51] + [[orgit-rev:~/src/code.unbl.ink/secstate/vrobbler/::93c16d80ecff4cd1663cf9ec40fbe6d8f58c3e44][~/src/code.unbl.ink/secstate/vrobbler/ (magit-rev 93c16d8)]] + + https://code.unbl.ink/secstate/vrobbler/commit/93c16d80ecff4cd1663cf9ec40fbe6d8f58c3e44 + +** DONE What to do with Youtube videos from LastFM and web-scrobbler :bug:source:lastfm: +- Note taken on [2025-04-04 Fri 10:46] + + Nothing. Over the last few months I built out a youtube model in videos and + use a bookmarklet scrobbling pattern. Now web-scrobbler is just disabled for + Youtube. + + May want to revisit this at some point and only scrobble tracks from Youtube, + because many people use YT for music listening. + +** DONE [#C] Consider a purge command for duplicated and stuck in-progress scrobbles :utililty:improvement: +CLOSED: [2023-04-06 Thu 14:09] +** DONE Add a "stop_timestamp" so we don't rely on content length :improvement:scrobbling: +CLOSED: [2023-04-02 Sun 23:58] + +Essentially, we currently have the timestamp as when the content began +scrobbling and then calculate the finish time from the length of the content. +This works pretty well because we know how long most things are. + +But in some cases, sports events or long podcasts, we may start mid-way through +an event or finish halfway through but still want to mark it as done. In these +cases, knowing the finish time could be useful, especially when interfacing with +other scrobblers which may have different definitions of when a scrobble +finishes or started. +** DONE Fix bug with Various Artist albums being labeled with first artist as album artist :scrobbling:bug:music: +CLOSED: [2023-03-27 Mon 20:18] +:LOGBOOK: +CLOCK: [2023-03-26 Sun 22:01]--[2023-03-27 Mon 01:07] => 3:06 +:END: +** DONE Fix bug with weekly aggregator being blank on Sundays :aggregators:music:bug: +CLOSED: [2023-03-26 Sun 13:52] +** DONE Fix KoReader scrobbling to use pages rather than time of last read :scrobbling:books:improvement: +CLOSED: [2023-03-26 Sun 13:51] +:LOGBOOK: +CLOCK: [2023-03-26 Sun 13:11]--[2023-03-26 Sun 13:51] => 0:40 +:END: +** DONE [#A] Add django-storage to store files on S3 :settings:improvement: +CLOSED: [2023-03-24 Fri 14:46] +:LOGBOOK: +CLOCK: [2023-03-24 Fri 10:47]--[2023-03-24 Fri 14:46] => 3:59 +CLOCK: [2023-03-24 Fri 10:36]--[2023-03-24 Fri 10:40] => 0:04 +:END: +** DONE Fix vrobbler settings not using booleans :settings:bug: +CLOSED: [2023-03-24 Fri 10:45] +:LOGBOOK: +CLOCK: [2023-03-24 Fri 10:40]--[2023-03-24 Fri 10:46] => 0:06 +:END: +** DONE Update weekly live chart to be 7-day continuous rather than weekly :views:bug: +CLOSED: [2023-03-24 Fri 00:31] +The live view will be blank every Monday, no reason to tie it to a day of the +week. It should be "the last 7 days" +** DONE [#B] Implement a detail view for TV shows :improvement:views: +CLOSED: [2023-03-22 Wed 17:05] +** DONE [#B] Implement a detail view for Movies :improvement:views: +CLOSED: [2023-03-22 Wed 17:05] +** DONE Add "service provider" to TV Series, and use that for source when available :bug:scrobbling: +CLOSED: [2023-03-22 Wed 17:04] +** DONE Add view for long-play content (books, video games) to restart them :views:improvement: +CLOSED: [2023-03-22 Wed 17:01] +** DONE Add live chart view like Maloja :improvement:views: +CLOSED: [2023-03-07 Tue 11:13] +** DONE [#C] Figure out how to add to web-scrobbler :improvement:scrobbling: +CLOSED: [2023-03-22 Wed 17:06] + +An example: +https://github.com/web-scrobbler/web-scrobbler/blob/master/src/core/background/scrobbler/maloja-scrobbler.js + +This is actually going to be moot because we can import from LastFM, and +web-scrobbler integrates well with LastFM. The only thing to think through here +now is what to do with all the garbage web-scrobbler sometimes pushes to LastFM +from Youtube (all videos get pushed, sigh). + +** DONE Add Amazon scraper to look up books when OL fails :books:improvement: +This turned out to be a non-starter ... Amazon is aggressive at disallowing +scraping quality. And all the OSS tools out there are stuck in an arms race +trying to keep them from breaking. + +That said, Google Books actually has a decent API (for now), and I've built this +out using that. + +** DONE Fix bug in Jellyfin scrobbles that spam more scrobbles after completion :scrobbling:videos:bug: +This was fixed a while ago, but there's a new manifested bug. Going to create a +separate bug tracking ticket for that. + +* Version 0.11.4 [9/9] +** DONE Add rudimentary video game scrobbling :improvement:content:videogames: +CLOSED: [2023-03-07 Tue 11:11] +** DONE Add ability to scrobble from KOReader statistics files :improvement:books:content: +CLOSED: [2023-03-07 Tue 11:11] + +** DONE [#A] Fix fetching artwork without release group :bug: +CLOSED: [2023-01-29 Sun 14:27] + +When we get artwork from Musicbrianz, and it's not found, we should check for +release groups as well. This will stop issues with missing artwork because of +obscure MB release matches. + +** DONE [#A] Fix Jellyfin music scrobbling N+1 past 90 completion percent :bug: +CLOSED: [2023-01-30 Mon 18:31] +:LOGBOOK: +CLOCK: [2023-01-30 Mon 18:00]--[2023-01-30 Mon 18:31] => 0:31 +:END: + +If we play music from Jellyfin and the track reaches 90% completion, the +scrobbling goes crazy and starts creating new scrobbles with every update. + +The cause is pretty simple, but the solution is hard. We want to mark a scrobble +as complete for the following conditions: + +- Play stopped and percent played beyond 90% +- Play completely finished + +But if we keep listening beyond 90, we should basically ignore updates (or just +update the existing scrobble) +** DONE [#A] Add support for Audioscrobbler tab-separated file uploads :improvement: +CLOSED: [2023-02-03 Fri 16:52] + +An example of the format: +#+begin_src csv +, +#AUDIOSCROBBLER/1.1 +#TZ/UNKNOWN +#CLIENT/Rockbox sansaclipplus $Revision$ +75 Dollar Bill I Was Real I Was Real 4 1015 S 1740494944 64ff5f53-d187-4512-827e-7606c69e66ff +75 Dollar Bill I Was Real I Was Real 4 1015 S 1740494990 64ff5f53-d187-4512-827e-7606c69e66ff +311 311 Down 1 173 S 1740495003 00476c23-fd9e-464b-9b27-a62d69f3d4f4 +311 311 Down 1 173 L 1740495049 00476c23-fd9e-464b-9b27-a62d69f3d4f4 +311 311 Down 1 173 L 1740495113 00476c23-fd9e-464b-9b27-a62d69f3d4f4 +311 311 Random 2 187 S 1740495190 530c09f3-46fe-4d90-b11f-7b63bcb4b373 +311 311 Random 2 187 L 1740495194 530c09f3-46fe-4d90-b11f-7b63bcb4b373 +311 311 Jackolantern’s Weather 3 204 L 1740495382 cc3b2dec-5d99-47ea-8930-20bf258be4ea +311 311 All Mixed Up 4 182 L 1740495586 980a78b5-5bdd-4f50-9e3a-e13261e2817b +311 311 Hive 5 179 L 1740495768 18f6dc98-d3a2-4f81-b967-97359d14c68c +311 311 Guns (Are for Pussies) 6 137 L 1740495948 5e97ed9f-c8cc-4282-9cbe-f8e17aee5128 +311 311 Misdirected Hostility 7 179 S 1740496085 61ff2c1a-fc9c-44c3-8da1-5e50a44245af +, +#+end_src +** DONE [#B] Allow scrobbling music without MB IDs by grabbing them before scrobble :improvement: +CLOSED: [2023-02-17 Fri 00:10] + +This would allow a few nice flows. One, you'd be able to record the play of an +entire album by just dropping the muscibrainz_id in. This could be helpful for +offline listening. It would also mean bad metadata from mopidy would not break +scrobbling. +** DONE When updating musicbrainz IDs, clear and run fetch artwrok :improvement: +CLOSED: [2023-02-17 Fri 00:11] +** DONE [#A] Add ability to manually scrobble albums or tracks from MB :improvement: +CLOSED: [2023-03-07 Tue 11:09] + +Given a UUID from musicbrainz, we should be able to scrobble an album or +individual track. + +** DONE [#C] Implement keeping track of week/month/year chart-toppers :improvement: +CLOSED: [2023-03-07 Tue 11:10] +:LOGBOOK: +CLOCK: [2023-01-30 Mon 16:30]--[2023-01-30 Mon 18:00] => 1:30 +:END: + +Maloja does this cool thing where artists and tracks get recorded as the top +track of a given week, month or year. They get gold, silver or bronze stars for +their place in the time period. + +I could see this being implemented as a separate Chart table which gets +populated at the end of a time period and has a start and end date that defines +a period, along with a one, two, three instance. + +Of course, it could also be a data model without a table, where it runs some fun +calculations, stores it's values in Redis as a long-term lookup table and just +has to re-populate when the server restarts. + +** DONE [#B] Fix ImproperlyConfigured for drink-detail in scrobbles API :bug:api:drinks: +:PROPERTIES: +:ID: 8f2a3b4c-5d6e-7f80-9102-a3b4c5d6e7f8 +:END: + +*** Description + +GET /api/v1/scrobbles/ raised ImproperlyConfigured because +HyperlinkedModelSerializer could not resolve drink-detail — no API +ViewSet existed for the Drink model. + +*** Solution +- Added DrinkSerializer and DrinkViewSet to drinks/api/ +- Registered DrinkViewSet as "drinks" in the DRF router +- Verified /api/v1/scrobbles/ resolves without error diff --git a/PROJECT.org b/PROJECT.org index 9bf5e94..8589eae 100644 --- a/PROJECT.org +++ b/PROJECT.org @@ -1,6 +1,7 @@ #+title: Vrobbler Project We should convert this PROJECT file to put tickets in a subdirectory, tickets, with each ticket having it's own shortid_title.org + * Overview Vrobbler began humbly enough as a way to use Jellyfin's webhook to keep track of @@ -17,176 +18,11 @@ tasks, Todoist tasks, web pages I've read and trails I've hiked has turned out to be sometimes cathartic and sometimes functional as I try to remember when I did a thing. - -* Features -** Beer -*** Triggers -**** Bookmarklet -**** Manual -*** Metadata sources -**** Untappd -** Book -*** Triggers -**** Webdav via KoReader -**** Manual -*** Metadata sources -**** Google Books -This is the preferred method at this time. Also, the Book model implements a -`find_or_create` classmethod which is an example of an interface we can use for -other data models to get metadata in a way that provides easy testing, bulk -fetching and simple saving. -**** OpenLibrary -**** ComicVine -** Board Game -*** Triggers -**** IMAP import -**** Bookmarklet -**** Manual -** Location -*** Triggers -**** GPSLogger (Android) -*** Metadata sources -**** User input -** Music -*** Triggers -**** Last.FM -**** Rockbox files -**** Mopidy -**** Jellyfin -*** Metadata sources -**** Musicbrainz -** Podcast -*** Triggers -**** Mopidy -*** Metadata sources -**** Google Podcasts -**** PodcastIndex -** Sport -*** Triggers -**** Bookmarklet -**** Manual -*** Metadata sources -**** Thes Sports DB -** Task -*** Triggers -**** Todoist -**** Org-mode -*** Metadata sources -**** User profile -** Trails -** Video -*** Triggers -**** Jellyfin -**** Bookmarklet -**** Manual -*** Metadata sources -**** IMDB -**** Youtube -** Web Page -*** Triggers -**** Bookmarklet -*** Metadata sources -**** Scraper - -* Android App [0/7] :android:mobile:api: -** TODO [#A] Generate Kotlin API client from OpenAPI spec :android:codegen: - -*** Description - -Use openapi-generator to produce a Kotlin Retrofit client from the schema. -This gives us typed data classes, request builders, and a service interface -for every endpoint. The generated code lives in a separate Gradle module -(api-client) so it can be tested independently. - -*** Tasks -- Add openapi-generator to project tooling -- Configure codegen for Kotlin + Retrofit + Moshi/Gson -- Generate client into android/api-client module -- Verify generated code compiles against the API -- Add a thin wrapper for token auth interceptor - -** DONE [#A] Scaffold Android project with Jetpack Compose :android:setup: - -*** Description - -Create the Android app module with a standard Compose architecture: single -activity, Navigation Compose, Hilt for DI, and a data layer backed by the -generated API client. Use Material 3 theming. - -*** Tasks -- [X] Create Android project (Kotlin, minSdk 26) -- [X] Add dependencies: Compose, Navigation, Hilt, OkHttp, Coil -- [X] Set up package structure: ui/, data/, di/, navigation/ -- [X] Add token storage (EncryptedSharedPreferences) -- [X] Add auth interceptor for OkHttp -- [X] Create MainActivity with NavHost - -** TODO [#B] Build scrobble list and detail screens :android:scrobbles: - -*** Description - -The core screen: a scrollable list of recent scrobbles grouped by date, with -a detail view showing the media item, timestamp, and any log data. Pull-to- -refresh. Infinite scroll pagination. - -*** Tasks -- ScrobbleListScreen: LazyColumn with date headers -- ScrobbleDetailScreen: media info, timestamps, log JSON -- ViewModel with Paging 3 integration -- Filter by media_type (bottom sheet or chips) -- Pull-to-refresh support - -** TODO [#B] Build media catalog screens :android:media: - -*** Description - -Browse screens for the major media types: tracks, videos, books, foods, beers, -board games, etc. Each is a list + detail pattern using the catalog endpoints -(not scrobbles). Start with the 5 most-used types and add more incrementally. - -*** Tasks -- TrackListScreen / TrackDetailScreen -- VideoListScreen / VideoDetailScreen -- BookListScreen / BookDetailScreen -- FoodListScreen / FoodDetailScreen -- BoardGameListScreen / BoardGameDetailScreen -- Shared list component (ReusableMediaList) to reduce duplication -- Search/filter per screen - -** TODO [#B] Add trend viewing screens :android:trends: - -*** Description - -Display the pre-computed trend data from the trends API. Start with the -overview (trend list with enable/disable), then individual trend detail -screens with the period selector (7d / 30d / 90d / 1yr). The trend -partials are HTML/JS charts — for mobile, either render them in a WebView -or extract the JSON data and render natively with a Compose charting -library. - -*** Tasks -- TrendListScreen: list of available trends with status -- TrendDetailScreen: period selector + chart rendering -- Decide chart approach (WebView vs native Compose charts) -- Period selector (horizontal chips for 7/30/90/365) - -** TODO [#C] Add scrobble creation from the app :android:create: - -*** Description - -Allow creating scrobbles directly from the app. This is lower priority -since most scrobbling happens via webhooks (Jellyfin, Mopidy, KoReader), -but useful for manual entries (food, drinks, board games, tasks, life -events). - -*** Tasks -- Scrobble creation form per media type (start simple with food + drink) -- Media item picker (search existing or create new) -- Timestamp picker (default to now) -- Optional: barcode scanning for food/drinks via CameraX - * Backlog [2/28] :vrobbler:project:personal: ** TODO [#C] After transition to linux add curl_cffi as webpage scrapper again :webpages:metadata: +:PROPERTIES: +:ID: d3cce1a7-d540-4d66-bf66-e75378e4eac7 +:END: ** TODO [#C] Create small utility to clean up tracks scrobbled with wonky playback times :bug:music:scrobbles: :PROPERTIES: :ID: 702462cf-d54b-48c6-8a7c-78b8de751deb @@ -794,2932 +630,3 @@ Import gym sessions and routines from a Flexify backup. shared/public rendering; owner views keep the full `log`/`logdata`. Defaults cover body-composition metrics on Task scrobbles and `bodyweight_kg` on Workout scrobbles. - -* Version 64.5 [1/1] -** DONE [#B] Clean up issues with org-mode notes :orgmode:notes:scrobbles: -:PROPERTIES: -:ID: aa3dae85-b3ea-49a8-a964-0418595de0d2 -:END: - -*** Description - -When syncing notes from org mode scrobbles, the header of the last note gets -pulled into the new one, so on a scrobble with multple notes "- Note taken on" -with a bunch of newlines ends up in the second comment. - -Second, if an org_id is detected, like this: -#+begin_src text -[[id:f6fc4950-07e6-459b-a256-c18282ca9256][Mobile Transition Plan: Cookbooks V3 Nested → Flattened Fields]] -#+end_src - -The text should be transformed into human readable markdown text like this: - -#+begin_src md -[Mobile Transition Plan: Cookbooks V3 Nested → Flattened Fields](id:f6fc4950-07e6-459b-a256-c18282ca9256) -#+end_src - -Also, source blocks from org mode should be converted to markdown source block. - - -* Version 64.4 [1/1] -** DONE [#A] Fix raw HTML output from agent response :agents:bug: -:PROPERTIES: -:ID: e9137445-4a6e-4394-a31e-5b0767df62cd -:END: - -* Version 64.3 [2/2] -** DONE [#B] Add notifications when agent prompts return responses :notifications:agents: -:PROPERTIES: -:ID: 339fa174-b50c-8033-88cb-6984ec62274f -:END: -** DONE [#A] Make agent prompts more resilient :agents:bug: -:PROPERTIES: -:ID: 514ea328-a02a-a950-0fbe-445a66577675 -:END: - -* Version 64.2 [1/1] -** DONE [#B] Fix a bug in koreader imports using a static tmp file :bug:koreader:importers: -:PROPERTIES: -:ID: 31dad69f-33b8-71aa-fef0-91c088fb7bb9 -:END: - -* Version 64.1 [2/2] -** DONE [#B] Add search for agent sessiosn :agents:search: -:PROPERTIES: -:ID: a050cc92-9f7b-7df0-d6a4-3c03ab7e5a25 -:END: -** DONE [#A] Add rc.d files for FreeBSD process management :admin: -:PROPERTIES: -:ID: 7d67c042-cfd5-e2f0-d714-03a20f16803f -:END: - -* Version 64.0 [2/2] -** DONE [#B] Add Google Gemini as a model option back for agent sessions :agents: -:PROPERTIES: -:ID: 0adecc94-2ce2-ed8f-4439-0738ea409c68 -:END: -** DONE [#A] Update agent flow to use OpenRouter and select model :agents: -:PROPERTIES: -:ID: 70f753a8-b320-4f35-1dcd-9f0d4f1c707b -:END: - -*** Description - -Currently the agent session is limited to only Google Gemini 3.5. We should -instead do something like how Foods works, where when a user enters a prompt -the next page should allow choosing one of the free models pulled from OR. - -From that point on, the scrobbles is of that agent model and the response should -come from there, with any susequent prompts going to thes ame model (with -context of past prompts and response). - -**** Tasks -- [X] Add openrouter_agent_prompt and list_openrouter_free_models to agents/providers.py -- [X] AgentModelSelectView lists free OpenRouter models with the pending prompt -- [X] AgentScrobbleFromModelView scrobbles the chosen model as an agent session -- [X] ManualScrobbleView routes unprefixed prompts to the model select page via session -- [X] scrobble_agent_session_prompt task passes the session model to the provider -- [X] Keep gemini and opencode provider routes working during the transition -- [X] Update settings, conf example and .envrc for OpenRouter -- [X] Write tests - - -* Version 63.4 [1/1] -** DONE [#A] Update agent flow to use OpenRouter and select model :agents: -:PROPERTIES: -:ID: 70f753a8-b320-4f35-1dcd-9f0d4f1c707b -:END: - -*** Description - -Unprefixed prompts in the scrobble bar no longer hit Gemini directly. They now -redirect to an agent model select page (like Foods search) that lists free -OpenRouter models (priced $0 for both prompt and completion) pulled from the -OpenRouter models API. Picking one scrobbles that model as an agent session with -the prompt as the title; follow-ups keep using the same model with conversation -context. gemini and opencode provider routes remain for the transition. - -**** Tasks -- [X] Add openrouter_agent_prompt and list_openrouter_free_models to agents/providers.py -- [X] AgentModelSelectView lists free OpenRouter models with the pending prompt -- [X] AgentScrobbleFromModelView scrobbles the chosen model as an agent session -- [X] ManualScrobbleView routes unprefixed prompts to the model select page via session -- [X] scrobble_agent_session_prompt task passes the session model to the provider -- [X] Keep gemini and opencode provider routes working during the transition -- [X] Update settings, conf example and .envrc for OpenRouter -- [X] Write tests - -* Version 63.3 [1/1] -** DONE [#A] Fix overuse of OSM API on reverse geocodes :bug:geolocations: -:PROPERTIES: -:ID: f8be6925-7d67-4ee4-9321-c570b4888f14 -:END: - -*** Description - -Nominatim rate limits (HTTP 429) were permanently dropping reverse geocodes -because the task swallowed the error and never retried, and nothing enforced -Nominatim's 1 req/sec policy. This adds a typed NominatimRateLimited exception, -a cross-worker redis throttle enforcing 1 req/sec, and Celery retry honoring -Retry-After with exponential backoff. Also added a reverse_geocode_missing -management command and switched the admin action to enqueue tasks. - -**** Tasks -- [X] Add NominatimRateLimited exception and redis throttle to locations/utils.py -- [X] Retry reverse_geocode_geolocation with Retry-After / exponential backoff -- [X] Add reverse_geocode_missing management command -- [X] Admin action enqueues tasks instead of blocking with a sleep -- [X] Add GEOLOC_REVERSE_GEOCODE_RATE / GEOLOC_REVERSE_GEOCODE_MAX_RETRIES settings -- [X] Write tests - -* Version 63.2 [1/1] -** DONE [#A] Swap around AI API key for Google so Youtube still works :bug:agents: -:PROPERTIES: -:ID: e3fc73ee-d310-b2e6-e0f1-36e92cbf94c3 -:END: - - -* Version 63.1 [1/2] -** DONE [#A] Fix agent scrobble bug with duration :scrobbles:agents: -:PROPERTIES: -:ID: 84704fe9-57bf-f02d-b483-5177d2930662 -:END: -* Version 63.0 [3/3] -** DONE [#A] Add agent-session scrobbling :scrobbles:agents: -:PROPERTIES: -:ID: 0114a3d8-b13e-bbb6-b461-c7005e9be680 -:END: - -*** Description - -What if we could "scrobble" an agent session? Honestly, this is mostly applicable to -web searches or quick questions via Gemini or similar web-based agents. - -The scrobble should be the tool, so using Gemini is an agent_session scrobble, and the -associated prompt and any response are stored in the AgentSessionLogData. For this, -we'd need a way to start a prompt with an AI agent via python libraries like genai. - -Then we'd ask a question or a prompt request in the top Scrobble something bar. In -this case, an entry without a - prefix would be understood as starting a -Gemini chat and would send the prompt to Gemini, get a response and return it -to the browser, starting a new scrobble and storing any response in the log data. - -Then the AgentSessionLogData would capture a list of turns: [{"prompt_id": , -"prompt": "A request prompt", "response": "An agent respones"}, - -**** Tasks -- [X] Add agents app with AgentSession model and AgentSessionLogData -- [X] Add gemini (httpx) and opencode (CLI) agent provider modules -- [X] Wire AgentSession into scrobbles (media type, FK, prefetching) -- [X] Route unprefixed manual scrobble input to agent sessions -- [X] Add async Celery task to fetch turn responses -- [X] Render agent turns on scrobble detail with htmx polling -- [X] Add agent session list/detail views and templates -- [X] Generate migrations -- [X] Write tests - -** DONE [#A] Auto-complete agent session scrobbles after 10 minutes :scrobbles:agents: -:PROPERTIES: -:ID: b4863178-7598-4bb7-9df7-6635d4851b57 -:END: - -*** Description - -Agent session scrobbles should start in progress and stay that way after the -turn response arrives so the session is visible as "Asking now" on the -homepage. Roughly ten minutes after the final turn's response, a Celery task -auto-completes the scrobble (in_progress = False). If a follow-up is submitted -while a turn is still pending, the auto-complete re-schedules itself. - -**** Tasks -- [X] Keep agent session scrobbles in progress after turn responses -- [X] Add scrobble_agent_session_complete Celery task (10 min auto-complete) -- [X] Re-schedule auto-complete when a turn is still pending -- [X] Write tests - -** DONE [#A] Fix small bug in nature importer if geojson is missing :importers:nature: -:PROPERTIES: -:ID: 0e0a9089-fda1-5e0d-6974-bebacb571cd3 -:END: - -* Version 62.2 [3/3] -** DONE [#A] Fix concurrent trend to be more useful :trends:concurrent: -:PROPERTIES: -:ID: b43652a8-31e3-6e0d-8489-5cc93864df84 -:END: -** DONE [#B] Add a public blog feature for scrobbles with notes :blog: -:PROPERTIES: -:ID: 0ae9449a-e107-39dd-cd47-7174021b5c18 -:END: -** DONE [#A] Add drf-spectacular for OpenAPI schema generation :api:documentation: -:PROPERTIES: -:ID: 2fddd5d0-1138-4a6e-96b7-5879035f033f -:END: - -*** Description - -Install drf-spectacular and configure it to generate an OpenAPI 3.0 schema for -all 48+ REST endpoints. This is a prerequisite for auto-generating the Kotlin -networking layer. Add the schema endpoint at /api/schema/ and the Swagger UI -at /api/docs/. - -**** Tasks -- [X] Add drf-spectacular to dependencies -- [X] Configure in REST_FRAMEWORK settings -- [X] Add schema and docs URL patterns -- [X] Verify schema generates cleanly for all ViewSets -- [X] Fix any serializer issues that block schema generation - - -* Version 62.1 [1/1] -** DONE Add open food stats as requirement -:PROPERTIES: -:ID: c6f47c5a-2c24-9e48-f1e5-edd11be5f36c -:END: - -* Version 62.0 [1/1] -** DONE [#B] Add open food facts source for Food and Drink :source:food:drinks: -:PROPERTIES: -:ID: d8a3e498-b03c-050d-a9bf-86e76eaa9201 -:END: - -* Version 61.5 [2/2] -** DONE [#A] Fix referer bug in OSM tiles :bug:osm:templates: -:PROPERTIES: -:ID: dcfbe119-d761-b0a4-fb03-6ce920fda6bf -:END: -** DONE [#A] Fix bugs in lifeevent urls :bug:urls: -:PROPERTIES: -:ID: ac6c0102-4de7-b6e4-65de-8231752d9a31 -:END: - -* Version 61.4 [1/1] -** DONE [#B] Add 7-day period for trends :trends: -:PROPERTIES: -:ID: 95731744-b0ec-175e-88dc-822cf9110292 -:END: - -* Version 61.3 [1/1] -** DONE [#A] Add trends tests and fix fasting :trends:tests:fasting: -:PROPERTIES: -:ID: 70c20539-110e-d4f4-30bd-e245152459c2 -:END: - -* Version 61.2 [1/1] -** DONE Fix bug in trends and add liquid fasting :food:fasting:trends: -:PROPERTIES: -:ID: 4736676e-95b0-cb4c-0eaa-c151f3753cd0 -:END: - -* Version 61.1 [1/1] -** DONE [#B] Add trend for fasting :food:fasting:trends: -:PROPERTIES: -:ID: a290b64d-14ad-49c2-addc-0d149c0c38fc -:END: - -*** Description - -Could we add a trend for number of fasting days over the time period? This would -look for the gap between the last and next Food scrobble and if it's ever longer -than a specified seconds configuration setting we'd mark it as a fasting day. -There can be "Periodic Fasting" which is defined by default as 16 hours or "Full -Fasting" which is designed as a 24 hour period without fasting. - -Fasting checking should also be skipped if it's during a LifeEvent of type -Vacation as vacations should be exempt. This exemption should be a userprofile -setting so folks can turn it on or off, and if it's changed the next trend -rebuild should consider that. - - - -* Version 61.0 [3/3] -** DONE [#B] Try adding a nature app with Observations and Species -:PROPERTIES: -:ID: 9551cf06-02b7-1bc9-3600-b0407ecc5967 -:END: - -*** Description - -iNaturalist has a feed in atom. - -** DONE [#A] Add ability to push one or sync all board game scrobbles to BGG :boardgames: -:PROPERTIES: -:ID: 1f306552-35e8-4a75-a661-b0956e8de967 -:END: - -*** Description - -We have a half-baked BGG sync module. I don't even know it if works. Can we -clean it up, add some tests and then add UI features to either sync a single -board game scrobble (via the detail page) to BGG, along with a "sync all" button -on the board game list page. Additionally, the sync should merge data between -Vrobbler and BGG based on timestamp and bgg_id. Priority should be given to data -from Vrobbler. - -** DONE [#C] Clean up naming of =bgsplay= parsing :importers:refactoring: -:PROPERTIES: -:ID: c751dbbc-464a-4e63-9fe3-e034303f7b54 -:END: - -*** Description - -We should rename `email_scrobble_board_game` to reflect the fact that it's just -a helper method to create board game scrobbles given a json blob. It's -independent of the email flow it was originally creatdd for - - -* Version 60.2 [1/1] -** DONE Use FastCork to lookup wine data :drinks:wine:metadata: -:PROPERTIES: -:ID: add9a951-d957-415d-b41e-feee40479774 -:END: - -* Version 60.1 [1/1] -** DONE [#B] Migrate coffee scrobbles to new Coffee model :drinks:mgmtcmd: -:PROPERTIES: -:ID: edaf1200-b58e-8512-b2a2-c8bc25bed7a0 -:END: - -*** Description - -Food ID 10 is "French Press Coffee with Sugar" and we should convert all of -these scrobbles to scrobbles of a Coffee, but to figure out which coffee, we can -use scrobbles of Task "Chore" where the title is "Roast coffee" and the notes -are not null. In this case, the first note on each one has a Coffee title like -"Mexico - Veracruz Huatusco" where the Origin is Mexico and the title is -"Veracruz Huatusco" - -We should create a new Coffee for each Chore with the title of "Roast Coffee" -and then for each "French Press Coffee with Sugar", swap the Food ID 10 for the -Coffee ID of the latest Coffee created from a Chore. If a new Chore is found, -start updating the French Press scrobbles with a newly created Coffee. - -If we don't have a roast log in the Chore tasks before a French Press entry, we should use the "Unknown Roast" Coffee title and skip all the othe metadata. - - -* Version 60.0 [2/2] -** DONE [#B] Send water drinking notifications :drinks:notifications: -:PROPERTIES: -:ID: 6c0db717-a0c7-34a1-57e1-941db0188875 -:END: -** DONE [#B] Add wine as a ScrobbleItem in a drinks app and move beers into drinks app :drinks:beer: -:PROPERTIES: -:ID: add1be0d-eb33-3273-37bf-0f2cab7e67c5 -:END: - -* Version 59.5 [1/1] -** DONE [#A] Fix bug where all variants for board games are in form :boardgames: -:PROPERTIES: -:ID: 9c4cd193-580a-5b33-8832-1feffea7bd53 -:END: - -* Version 59.4 [2/2] -** DONE Fix bug in fetching expansions for board games :boardgames: -:PROPERTIES: -:ID: 17995312-e76e-4a50-b591-0eab78cb59ab -:END: - -#+begin_src python - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/boardgames/management/commands/fetch_expansions.py", line 60, in handle - fetch_and_link_expansions(game, expansions) - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/boardgames/utils.py", line 51, in fetch_and_link_expansions - expansion = BoardGame.find_or_create(str(exp_id)) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/boardgames/models.py", line 409, in find_or_create - bgg_data = lookup_boardgame_from_bgg(lookup_id=lookup_id) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/boardgames/sources/bgg.py", line 15, in lookup_boardgame_from_bgg - game = bgg.game(game_id=lookup_id) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/boardgamegeek/api.py", line 1045, in game - raise BGGApiError(msg) -boardgamegeek.exceptions.BGGApiError: invalid data for game id: 242117 -#+end_src - -** DONE Board games should have genres extracted from family data :boardgames:metadata: -:PROPERTIES: -:ID: 7214b270-dccc-4b98-ac58-ff4f76c8cda9 -:END: - - -* Version 59.3 [2/2] -** DONE Exclude some board games from auto-expansion imports :boardgames: -:PROPERTIES: -:ID: 51ffdf20-e732-4774-b781-c3501d26d46f -:END: - -*** Description - -Some board games, especially trading card games, have silly amounts of expansions. -We should have a setting SKIP_AUTO_EXPANSION_DOWNLOAD that is a list of BGG ids of -games where expansions should not be download automatically. This exclusion should also auto-include -any games with "Collectible Card Games" in it's family. - -** DONE Should be able to add new variants to board games via the log data form :boardgames: -:PROPERTIES: -:ID: 5ed0dd25-3026-3da8-dc5c-f2a75751af9a -:END: - -* Version 59.2 [1/1] -** DONE Fix test failure in discgolf app :discgolf:tests: -:PROPERTIES: -:ID: 813ae357-0568-5a4c-1a35-172e95d02740 -:END: - -* Version 59.1 [1/1] -** DONE Fix bug when expansions have no image :boardgames:bug: -:PROPERTIES: -:ID: 9fee96c9-c6a0-32d9-b6f8-212c60fc3540 -:END: - -* Version 59.0 [3/3] -** DONE [#A] Add BoardGameVariant model :boardgames: -:PROPERTIES: -:ID: 0ffb20d5-252f-b13d-473d-5529014602ff -:END: - -*** Description - -Variants represent unique boards being used per scrobble or scenarios when -playing a game. Scrobbles of a board game may have one or more -boardgame_variant_ids assocaited with their log data, and a variant is created -for one specific board game. - - -** DONE [#A] Lookup all Expansions for a game when creating it :boardgames: -:PROPERTIES: -:ID: 8a84b06d-555c-4701-9058-ff364c89c198 -:END: - -*** Description - -We don't want to blow up the BGG API, but if possible with not too -many calls, when we scrobble a board game, in order to allow -populating the "Expansions" multi select, we should fetch any -expansions for the board game when creating it for the first time. - -We should also create a managemnt script to update existing board games. - -** DONE [#A] Board game expansion lookup should be async on URL scrobbles :boardgames: -:PROPERTIES: -:ID: 968d8dde-f906-cdf0-af4e-b87ce28ddbbb -:END: - -* Version 58.8 [1/1] -** DONE [#B] Clean up trend templates :trends:templates: -:PROPERTIES: -:ID: 83237e2c-857b-47c9-c86c-32a5e3f1359d -:END: - -* Version 58.7 [2/2] -** DONE [#B] Split up chart page between tables and maloja :charts:templates: -:PROPERTIES: -:ID: 103ab084-2016-cfa4-c677-3c5fdc54cce0 -:END: -** DONE [#A] Fix CI so we don't double run deploys and builds :ci: -:PROPERTIES: -:ID: 1a93e7cb-b883-aae5-2bd5-fcdd6e16f8ab -:END: - -* Version 58.6 [1/1] -** DONE [#B] Cleanup commands should check for broken images :metadata:cleanup: -:PROPERTIES: -:ID: bacce321-73c7-ae1f-bfa7-c3ee517b5441 -:END: - -* Version 58.5 [1/1] -** DONE [#A] The maloja style charts are messed up :templates:charts: -:PROPERTIES: -:ID: 987397a2-7e74-4eb1-87cc-4c8bbe1c7b23 -:END: - -* Version 58.4 [2/2] -** DONE [#B] Allow people all trends or individual trends :trends:profiles: -:PROPERTIES: -:ID: 1d081152-abd1-73c2-a625-903565a10c6c -:END: -** DONE [#A] Fix a bug in board game scorelog data :boardgames:logdata: -:PROPERTIES: -:ID: 014bab30-13bf-fae7-e678-4666a8d38ae4 -:END: - -* Version 58.3 [1/1] -** DONE [#A] Remove curl-cffi as it doesn't work on FreeBSD :webpages:deps: -:PROPERTIES: -:ID: 6bc1b0dd-e449-3d32-a176-46451e793e5d -:END: - -* Version 58.2 [2/2] -** DONE [#B] Add more robust webpage scraping :webpages:metadata: -:PROPERTIES: -:ID: 84d9bfa5-75c0-0718-764e-379f7456602a -:END: -** DONE [#B] Time of Day Categories trend :trends: -:PROPERTIES: -:ID: 6598074f-2290-46db-967b-29f45d30be29 -:END: - -*** Description - -Added a "Time of Day Categories" trend that groups scrobbles for Books, Trails, -Birding Locations, and Board Games into Early Bird (5-10:59am), Day Jay (11am-6:59pm), -and Night Owl (7pm-4:59am) buckets. Shows both overall and per-media-type breakdowns. - - -* Version 58.1 [1/1] -** DONE [#B] Add auto genre tagging for papers :books:papers:metadata: -:PROPERTIES: -:ID: e6b5c3a5-7fc6-b530-96c2-b5962a716db6 -:END: - -* Version 58.0 [1/1] -** DONE [#B] Add scrobbling of Papers via webpages with doi.org links in them :feature:papers: -:PROPERTIES: -:ID: d30bb8aa-eefd-002c-38d5-3f2fcef345f2 -:END: - -* Version 57.1 [1/1] -** DONE [#A] Write poetry lock file :bug:deps: -:PROPERTIES: -:ID: 0f5a6f4b-a486-ba7e-bbce-f7581274398c -:END: - -* Version 57.0 [5/5] -** DONE [#A] Scrobble button on some media list pages dont work :bug:scrobbles: -:PROPERTIES: -:ID: a3a5c707-2e3d-a6b1-0f7f-4c6f7433aa1f -:END: -** DONE [#B] Use HTMx to update the Now Playing widget :feature:templates: -:PROPERTIES: -:ID: 5f5631fc-9ee1-d5a5-d0f8-94fea6fbbfa4 -:END: -** DONE [#B] Add a live page that updates the scrobble list via JS polling :feature:templates: -:PROPERTIES: -:ID: 58790d76-dc6e-8aa5-2dc0-e64fe786fbf1 -:END: -** DONE [#A] Turns out we cant cache the now playing widget :bug:templates: -:PROPERTIES: -:ID: 9ce669ea-c000-cdfe-a634-ad5cdaeae81c -:END: -** DONE [#C] What would it look like to add an MCP server to expose scrobbles and media items? :mcpserver:feature: -:PROPERTIES: -:ID: c5fca159-c7e0-5795-7c05-bbc48f539650 -:END: - -* Version 56.4 [3/3] -** DONE [#B] Add ability to do reverse address lookup on lat-long pairs :geolocations:feature: -:PROPERTIES: -:ID: 86c071ff-7638-41ba-6b65-1382df1cb5aa -:END: -** DONE [#B] Add address fields to GeoLocation :addresses:geolocation: -:PROPERTIES: -:ID: a55ae508-07ab-ccdd-e453-846bd3fca6fb -:END: -** DONE [#B] Add better detail template for Disc Golf Courses :discgolf:templates: -:PROPERTIES: -:ID: 12cee67c-f723-0fa3-848a-cbc6e4d65fc3 -:END: - -* Version 56.3 [1/1] -** DONE [#A] Fix bug in importer script from discgolf being added :bug: -:PROPERTIES: -:ID: c3733f96-18f1-eef8-f5d9-edaf97e35623 -:END: - -* Version 56.2 [1/1] -** DONE [#A] Fix bug in creating people when importing course plays :discgolf:bug: -:PROPERTIES: -:ID: 255e9886-098b-39ae-1077-25e43223660e -:END: - -* Version 56.1 [1/1] -** DONE [#A] Add tests to discgolf app :discgolf:tests: -:PROPERTIES: -:ID: 28e8344e-c3cf-19af-ce1c-cb821d4fcb5f -:END: - -* Version 56.0 [1/1] -** DONE [#B] Add DiscGolf as a scrobbleable media :discgolf: -:PROPERTIES: -:ID: 8cdde5d3-0ae5-7d5a-99d2-200c86afae03 -:END: - -*** Description - -I have a csv file fro the uDisc disc golf scoring app that looks like: - -Singles round. Note second row is the par for the course -#+begin_src csv -PlayerName,CourseName,LayoutName,StartDate,EndDate,Total,+/-,RoundRating,Hole1,Hole2,Hole3,Hole4,Hole5,Hole6,Hole7,Hole8,Hole9 -Par,DR Front 9,Custom Layout,2026-06-19 1535-0400,2026-06-19 1725-0400,27,,,3,3,3,3,3,3,3,3,3 -Colin Powell,DR Front 9,Custom Layout,2026-06-19 1535-0400,2026-06-19 1725-0400,30,3,,2,3,4,4,3,4,3,3,4 -Asa Sewell,DR Front 9,Custom Layout,2026-06-19 1535-0400,2026-06-19 1725-0400,44,17,,5,4,4,8,5,5,4,4,5 -Emma Sweet,DR Front 9,Custom Layout,2026-06-19 1535-0400,2026-06-19 1725-0400,41,14,,5,4,5,6,3,4,3,5,6 -Jane Sewell,DR Front 9,Custom Layout,2026-06-19 1535-0400,2026-06-19 1725-0400,44,17,,4,5,5,5,5,5,4,6,5 -Nabby Sewell,DR Front 9,Custom Layout,2026-06-19 1535-0400,2026-06-19 1725-0400,59,32,,6,6,7,7,6,7,6,6,8 -Silas Sewell,DR Front 9,Custom Layout,2026-06-19 1535-0400,2026-06-19 1725-0400,41,14,,5,5,4,5,3,5,4,4,6` -#+end_src - -Teams of two or more persons. Note second row is the par for the course -#+begin_src csv -PlayerName,CourseName,LayoutName,StartDate,EndDate,Total,+/-,RoundRating,Hole1,Hole2,Hole3,Hole4,Hole5,Hole6,Hole7,Hole8,Hole9 -Par,Peninsula Links,Main,2026-06-19 2322-0400,2026-06-19 2323-0400,27,,,3,3,3,3,3,3,3,3,3 -Colin Powell + Asa Sewell,Peninsula Links,Main,2026-06-19 2322-0400,2026-06-19 2323-0400,29,2,,3,4,2,3,2,3,5,3,4 -Emma Sweet + Jane Sewell,Peninsula Links,Main,2026-06-19 2322-0400,2026-06-19 2323-0400,28,1,,4,3,4,2,3,4,3,3,2 -#+end_src - -We should add a new app called discgolf that has the following data models: - -- DiscGolfRound - scrobblable media + course_id, round_type (Singles, Teams) -- DiscGolfCourse - name, layoutname, number_of_holes - -And the logdata for a DiscGolfOuting scrobble should have: - + {person: {hole_number: score}, total: int} - + {team: {name: "", people: [person, person], hole_number: score}, total: int} - + weather - + fun_factor (miserable, not great, so-so, good, excellent, party time) - - -* Version 55.6 [1/1] -** DONE [#A] Figure out why historical Lastfm imports don't work :importers:lastfm:music: -:PROPERTIES: -:ID: 71b18c1b-de96-6d93-20fa-de2ec0df1288 -:END: - -* Version 55.5 [1/1] -** DONE [#B] Fix bug in lastfm import for new users :importers:lastfm:music: -:PROPERTIES: -:ID: d034966d-0c7f-e512-4cf8-7329c9026b6f -:END: - -* Version 55.4 [1/1] -** DONE [#A] Tighten up the speed of startup and first request :perf: -:PROPERTIES: -:ID: 9ee8834c-6be2-d04b-df6d-56375504083f -:END: - - -* Version 55.3 [3/3] -** DONE [#C] =alt_names= feature for artists (commented out / dead code) :music:dead-code: -:PROPERTIES: -:ID: e22060a2-5f7a-4f33-9056-309ecd27159c -:END: - -*** Description - -File: ~vrobbler/apps/music/models.py~ (line 236) - -An entire block of code for tracking alternate artist names is commented -out. The TODO questions whether it even works. Review: either implement -properly or remove the dead code. - -** DONE [#A] Put chart rebuilds in a lower priority task queue :charts:tasks: -:PROPERTIES: -:ID: 43c90de0-fc1c-1139-dac7-9b7c82006b2e -:END: -** DONE [#A] Check for existing book scrobble and update page count :books:scrobbling: -:PROPERTIES: -:ID: 1a0609bc-6b16-4da4-96c1-59588229e4b4 -:END: - -*** Description - -File: ~vrobbler/apps/scrobbles/scrobblers.py~ (line 330) - -When scrobbling a book (comic), the code doesn't check for prior scrobbles to -update reading progress. Needed for proper page-count tracking. - - -* Version 55.2 [2/2] -** DONE [#A] Fix bug in scrobble id in calendar view :templates: -:PROPERTIES: -:ID: 8cb34852-b18f-e794-cd9b-fb1ecad70a0d -:END: -** DONE [#A] Video game cleanup script should clear out broken images :metadata:videogames: -:PROPERTIES: -:ID: ca1f1ea9-0f79-082c-5ff7-867671faff4b -:END: - -* Version 55.1 [1/1] -** DONE [#A] Clean up metadata scrapping for video games :metadata:videogames: -:PROPERTIES: -:ID: fbc421b5-21a3-4aed-9062-c59192ead065 -:END: - -* Version 55.0 [3/3] -** DONE [#B] Use pk ID for scrobble detail view, not uuid :scrobbles: -:PROPERTIES: -:ID: 9cc3b285-e478-041e-394b-3d550aefbe1d -:END: -** DONE [#B] Display videogame screenshots on scrobble detail if they exist :videogames:templates: -:PROPERTIES: -:ID: 0406d082-20f6-0d12-76e2-f281c4801468 -:END: -** DONE [#B] Add autotagging to webpages based on domain, title :webpages:metadata: -:PROPERTIES: -:ID: f658435b-f7a0-42e6-b9f6-226678a77a55 -:END: - -*** Description - -For easier filtering, like we do with tasks, we should auto tag WebPage instances -based on the domain name split part by periods (so news.ycombinator.com tags: news, ycombinator, com) - -And also based on the nouns in the title. - - -* Version 54.5 [1/1] -** DONE Fix bug in generating mood trends :trends: -:PROPERTIES: -:ID: 8e75abfa-8e70-d85b-00a4-a4813bbce879 -:END: - -* Version 54.4 [2/2] -** DONE [#A] Remove all-time trends :trends: -:PROPERTIES: -:ID: 53b231d1-7677-8cd3-1d88-dae110aba1e6 -:END: - -*** Description - -All time trends take forever to calculate and don't provide too much data - -** DONE [#B] Add a trend around moods :moods:trends: -:PROPERTIES: -:ID: fba3f4ae-8f97-ee0b-e762-31630884518a -:END: - -* Version 54.3 [1/1] -** DONE [#B] Fix bug in series metadata cleanup script :videos:metadta: -:PROPERTIES: -:ID: 85448702-907c-5d63-f5af-7795661d7c46 -:END: - -* Version 54.2 [4/4] -** DONE [#B] Add script to clean up TV series metadata :videos:metadata: -:PROPERTIES: -:ID: a468b328-59d9-f84b-9ddb-087216783453 -:END: -** DONE [#A] Update youtube video detail pages with links to channel :videos:templates: -:PROPERTIES: -:ID: 8b87cb42-09e5-a3f5-136f-182f967fa81f -:END: -** DONE [#A] Concurrent reading trend does not consolidate on single book :trends:reading: -:PROPERTIES: -:ID: fe220f55-7e0d-2a17-2477-a5aa7c4a1f2c -:END: -** DONE [#B] Trends dont seem to look very far back :trends: -:PROPERTIES: -:ID: ffcfba3f-5a93-9ee0-9680-666e6eccd684 -:END: - -*** Description - -Specificially, looking at reading-pace when run on prod, it claims that I've -only had one reading session without music. Which may be true, but perhaps we -need to indicate what the time frame we're looking at is (month, week, year) -and provide a way to jump back and forward through time, same as charts. - - -* Version 54.1 [1/1] -** DONE [#A] Concurrent listening trend is inefficient and should be disabled :trends:scrobbles: -:PROPERTIES: -:ID: 4aa3b719-6b22-cae9-85f0-fac67b4fc753 -:END: - -* Version 54.0 [3/3] -** DONE [#B] Add peak hour, weekly rhythm and activity dist trends :trends:scrobbles: -:PROPERTIES: -:ID: 5fa52fac-d5f0-4369-bcaa-589c886b07d3 -:END: - -** DONE [#A] Implement YouTube channel info scraping :videos:youtube:stub: -:PROPERTIES: -:ID: 1d3beafd-62cb-4735-a465-edb37bf885db -:END: - -*** Description -File: ~vrobbler/apps/videos/models.py~ (line 140) - -=Video.fix_metadata()= is a stub that logs "Not implemented yet" and returns. -Needs actual implementation to scrape channel metadata from YouTube. - -** DONE [#A] Fix Amazon book scraper :amazon:scraper:broken: -:PROPERTIES: -:ID: c38aba25-0171-49ab-a9f3-acf2003da429 -:END: - -*** Description - -File: ~vrobbler/apps/books/amazon.py~ (line 56) - -The =scrape_data_from_amazon()= function is likely broken due to Amazon blocking -scrapers and changing HTML structure. Needs rewrite or replacement with a proper -API. - - -* Version 53.1 [1/1] -** DONE [#A] Error with loading logdict :scrobbles:bug:logdata: -:PROPERTIES: -:ID: 92d4fa16-4b90-47e0-95ae-472bdca582ce -:END: - - -* Version 53.0 [5/5] -** DONE [#B] Add a /trends/ page that shows trends based on scrobble data :feature:trends:scrobbles: -:PROPERTIES: -:ID: 03e9fe30-2bc6-4062-bb24-e95b98daf05b -:END: - -*** Description - -This project is a bit invovled. But we should add a top level URL `trends` that shows -various trends as defined either in a static settings file, or dynamically via a database table. - -Trends could be things like doing multiple things at the same time, like while driving, what -did we listen to this week, or while running, what were listening to this week? - -Or more complicated trends like, how time per page changes based on the book I was reading, or if I was doing something else (music or sport event) while reading. - -** DONE [#B] Notify users when Last.fm import completes :importers:notifications: -:PROPERTIES: -:ID: 92846b36-54c5-4b78-9c57-bdc401045fbe -:END: - -*** Description - -After a bulk import from Last.fm, users receive no confirmation. Should add a notification (in-app, email, or similar). - -File: ~vrobbler/apps/scrobbles/importers/lastfm.py~ (line 96) - -** DONE [#C] Cleaner =GeoLocationLogData= deserialization :models:refactoring: -:PROPERTIES: -:ID: 85465dbf-69b3-48cb-9df0-cd076c4470ab -:END: - -*** Description -Currently special-cases =GeoLocationLogData= by reaching into a nested ="movement_detection"= key. Should be handled at the LogData dataclass level. - -File: ~vrobbler/apps/scrobbles/models.py~ (line 977) - -** DONE [#B] Webpage scrobbles should diff existing webpages content :webpages:metadata: -:PROPERTIES: -:ID: 25576197-258f-48d6-bfe9-e4172a0a1898 -:END: - -*** Description - -Webpages change content between scrobbles. The current model stores the webpage content once, the -first time it's scrobbled. When a page has been seen before, we should move the existing content -to a new model HistoricalWebPage with the following fields: - -webpage_id -> FK to WebPage -date -> date from existing WebPage content -domain -> same as existing WebPage content -extract -> copy of existing WebPage content - -Once the HistoricalWebPage instance is successfully created, the new extract data -should be saved into the WebPage instance. - - -** DONE [#B] Make ArchiveBox push asynchronous :archivebox:async: -:PROPERTIES: -:ID: 17c116a7-5952-db37-e56c-2987c2fc456b -:END: -*** Description - -=push_to_archivebox()= runs synchronously during the request. Should be moved to a -Celery task or similar background worker. - -File: ~vrobbler/apps/webpages/models.py~ (line 133) - - -* Version 52.2 [1/1] -** DONE [#A] Fix bug in recomputing long play seconds taking forever :bug:longplay:commands: -:PROPERTIES: -:ID: 0a813cf9-17fb-dbd7-b5a7-7410d9bd4d8c -:END: - -* Version 52.1 [1/1] -** DONE [#C] Show time per scrobble in long play lists and total time playing :templates:longplay:scrobbles: -:PROPERTIES: -:ID: b3d16230-8ec5-46db-b166-59e98d0ee06c -:END: - -*** Description - -Long play time should be show in the table of scrobbles on a media detail page. -The total time spent in a long play that's either no completed yet or completed -should be displayed as well. If completed, the date finished should be shown as -well. - - -* Version 52.0 [5/5] -** DONE [#B] Allow marking media as long play complete from detail page :templates:scrobbles:longplay: -:PROPERTIES: -:ID: 2c314768-be97-9b10-d13c-9cfd0f38a64e -:END: -** DONE [#A] Fix how long play scrobbles are tracked :scrobbles:longplay:serial: -:PROPERTIES: -:ID: 908b0493-cabf-40c1-825f-cd59a8ad0f7a -:END: - -*** Description - -Currently we have this idea of "long_play" scrobbles but there's a lot missing -to tie it together. - -What we'd prefer is that when a new scrobble is added for a media_type that -`is_long_play` the most recent scrobble finished is added as the -`last_serial_scrobble` to the log data. But all the other long play stuff exsits -as data model fields. We should add `long_play_last_scrobble` as a FK to this -scrobble when creating a new longplay scrobble. - -Additionally, `long_play_seconds` we should have a recompute management command -to walk backward from `long_play_last_scrobble` until a `long_play_complete` -scrobble is found (exclusive) and save the time. - -We should also ony use `long_play_complete` field on the scrobble ... some -logdatas have a similar field, but we should make sure that we always use the -model field to determine if a long play is finished. - -This should include a command to clean up long play data to consolidate around -the `long_play_complete` field. - - -** DONE [#B] Paginate or limite scrobbles on media admin pages :admin:scrobbles:media: -:PROPERTIES: -:ID: f02e487b-d7ed-4834-838a-303560f2ad3b -:END: - -** DONE [#B] Clean up books admin :admin:books:bug: -:PROPERTIES: -:ID: 7539bee6-0a52-26f6-ebc6-5554ac49a716 -:END: -** DONE [#B] Clean up favorites admin :admin:favorites:scrobbles: -:PROPERTIES: -:ID: f2be0c69-1bf8-b5a3-5269-9c8ea873361d -:END: - - -*** Description - -Some FK lookups in admin should be raw_id_fields. - - - -* Version 51.4 [1/1] -** DONE [#A] Clean up metadata comicbook enrichment :bug:comics:books:metadata: -:PROPERTIES: -:ID: cd875450-7117-78ca-8be4-9c8b73037dba -:END: - -*** Description - -Still getting wonky results with some comicbooks. Would be nice to be able to -tag a Book as a comicbook, and also gather volume information. I also noticed -that some books that are found in OL never get their comicvine_id populated. We -should make sure we always have comicvine_ids if available. - - -* Version 51.3 [1/1] -** DONE [#A] Improve speed of index and chart pages :bug:scrobbles:perf: -:PROPERTIES: -:ID: 031a23f8-7c02-4926-9884-6654ceca16c2 -:END: - -*** Description - -Over the last few releases, the home page and charts pages have gotten really -slow. - -We should look into what's causing the slowness and maybe do more agressive -query optimization or caching. - - -* Version 51.2 [2/2] -** DONE [#A] Fix bug where last page of book gets separate scrobble :bug:books:importers:koreader: -:PROPERTIES: -:ID: e13e0b4c-461e-e5a9-c685-b972f4e262e5 -:END: - -*** Description - -The new KoReader code is working great to import books with the correct timezone -and what-not. But it has a weird artifact of creating one extra scrobble for the -last page read. Need to button that up. - -** DONE [#B] Fix metadata scraping for books :books:metadata: -:PROPERTIES: -:ID: ea416a69-a8a8-4d05-b7d4-0a3470820e34 -:END: - - -* Version 51.1 [1/1] -** DONE [#A] Fix scrobbling comic books :books:scrobbles:bug: -:PROPERTIES: -:ID: 8dfbff19-3fa4-f3b8-21c7-7a416498000c -:END: - -*** Description - -At some point logdata and log got confused, and now when you try -to scrobble a comic book, it just throws errors. We should look -into where the confusion happened and fix it. - - -* Version 51.0 [3/3] -** DONE [#B] Fix koreader scrobble imports to use DST properly :bug:books:imports: -:PROPERTIES: -:ID: 79758cba-a440-48b6-a637-efb88827acf2 -:END: - -- Note taken on [2025-09-25 Thu 10:37] \\ - - This may already be fixed ... need to check. - -- Note taken on [2025-02-25 12:34] \\ - - The page data has the canonical date something was read in it, but it seems - to be an hour off. I traced this back to being off during DST, so we just need - the importer to be aware of whether a user is using DST or not and roll back - an hour for part of the year. Also, we'd need to adjust any old scrobbles that - took place with DST off to roll them back by an hour. - - -*** Description - -This is a long-standing problem when daylight saving time takes effect. Time is -manually set on a KoReader device (or at least, always saved in local time). So -whatever time KoReader reports, we need to know, given the date and the user -profile's historic timezone, how many hours to adjust the KoReader time to get -to GMT to save it in the database. - -** DONE [#A] Fix book scrobbles where page_data is a list :bug:books:scrobbles: -:PROPERTIES: -:ID: 35b323fa-ccc0-4009-b227-8a0f12bbd469 -:END: - -*** Description - -Comic scrobbling is currently kind of janky. Most of the problems boil down to them -storing saved page data in a list of dicts rather than a dict keyed off of the page number that -was read. - -We need to adjust comic scrobbling to use a dict of pages keyed off the page number, and also -write a migration script that runs as a data migration to update any book scrobbles that may -have page_data as a list. - -*** Example data -#+begin_src python -{"notes": null, "page_end": 27, "page_data": [{"notes": null, "end_ts": 1771815895, "duration": 14, "start_ts": 1771815881, "description": null, "page_number": "1"}, {"notes": null, "end_ts": 1771815908, "duration": 13, "start_ts": 1771815895, "description": null, "page_number": "1"}, {"notes": null, "end_ts": 1771815913, "duration": 5, "start_ts": 1771815908, "description": null, "page_number": "2"}, {"notes": null, "end_ts": 1771815933, "duration": 20, "start_ts": 1771815913, "description": null, "page_number": "3"}, {"notes": null, "end_ts": 1771815945, "duration": 12, "start_ts": 1771815933, "description": null, "page_number": "4"}, {"notes": null, "end_ts": 1771815983, "duration": 38, "start_ts": 1771815945, "description": null, "page_number": "5"}, {"notes": null, "end_ts": 1771816007, "duration": 24, "start_ts": 1771815983, "description": null, "page_number": "6"}, {"notes": null, "end_ts": 1771816011, "duration": 4, "start_ts": 1771816007, "description": null, "page_number": "7"}, {"notes": null, "end_ts": 1771816013, "duration": 2, "start_ts": 1771816011, "description": null, "page_number": "8"}, {"notes": null, "end_ts": 1771816052, "duration": 39, "start_ts": 1771816013, "description": null, "page_number": "7"}, {"notes": null, "end_ts": 1771816127, "duration": 75, "start_ts": 1771816052, "description": null, "page_number": "8"}, {"notes": null, "end_ts": 1771816134, "duration": 7, "start_ts": 1771816127, "description": null, "page_number": "9"}, {"notes": null, "end_ts": 1771816196, "duration": 62, "start_ts": 1771816134, "description": null, "page_number": "10"}, {"notes": null, "end_ts": 1771816262, "duration": 66, "start_ts": 1771816196, "description": null, "page_number": "11"}, {"notes": null, "end_ts": 1771816293, "duration": 31, "start_ts": 1771816262, "description": null, "page_number": "12"}, {"notes": null, "end_ts": 1771816322, "duration": 29, "start_ts": 1771816293, "description": null, "page_number": "13"}, {"notes": null, "end_ts": 1771816330, "duration": 8, "start_ts": 1771816322, "description": null, "page_number": "14"}, {"notes": null, "end_ts": 1771816368, "duration": 38, "start_ts": 1771816330, "description": null, "page_number": "15"}, {"notes": null, "end_ts": 1771816388, "duration": 20, "start_ts": 1771816368, "description": null, "page_number": "16"}, {"notes": null, "end_ts": 1771816482, "duration": 94, "start_ts": 1771816388, "description": null, "page_number": "17"}, {"notes": null, "end_ts": 1771816550, "duration": 68, "start_ts": 1771816482, "description": null, "page_number": "18"}, {"notes": null, "end_ts": 1771816567, "duration": 17, "start_ts": 1771816550, "description": null, "page_number": "19"}, {"notes": null, "end_ts": 1771816586, "duration": 19, "start_ts": 1771816567, "description": null, "page_number": "20"}, {"notes": null, "end_ts": 1771816597, "duration": 11, "start_ts": 1771816586, "description": null, "page_number": "21"}, {"notes": null, "end_ts": 1771816616, "duration": 19, "start_ts": 1771816597, "description": null, "page_number": "22"}, {"notes": null, "end_ts": 1771816640, "duration": 24, "start_ts": 1771816616, "description": null, "page_number": "23"}, {"notes": null, "end_ts": 1771816690, "duration": 50, "start_ts": 1771816640, "description": null, "page_number": "24"}, {"notes": null, "end_ts": 1771816702, "duration": 12, "start_ts": 1771816690, "description": null, "page_number": "25"}, {"notes": null, "end_ts": 1771816823, "duration": 121, "start_ts": 1771816702, "description": null, "page_number": "26"}, {"notes": null, "end_ts": null, "duration": null, "start_ts": 1771816823, "description": null, "page_number": "27"}], "page_start": 1, "pages_read": 27, "resume_url": null, "description": null, "koreader_hash": null, "long_play_complete": false} - -#+end_src -** DONE [#A] Lichess imports do not set default visbility :boardgames:bug:importers:lichess: -:PROPERTIES: -:ID: a78f7c72-a20a-8db2-cde0-d92a731d4fba -:END: - -* Version 50.2 [2/2] -** DONE [#B] Koreader imports only import single-page scrobbles the next day :bug:books:importers: -:PROPERTIES: -:ID: b50141fd-cda6-4a3a-afd3-cd8499e7523e -:END: - -*** Description - -When you read a single page in a book in Koreader and try to import it, the scrobble is only -created the day after, not on the day of the reading. - -** DONE [#A] Fix bugs in celery tasks causing imports to fail :bug:celery:tasks: -:PROPERTIES: -:ID: d1171cb0-6413-44b8-a68a-019a4d2fb285 -:END: - -*** Description - -Seems like all celery tasks are failing for different reasons except the chart -updates. - -*** Errors -**** scrobbles.tasks.send_notification_for_in_progress -#+begin_src bash -KeyError: 'track' - -During handling of the above exception, another exception occurred: - -Traceback (most recent call last): -File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute -return self.cursor.execute(sql, params) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -psycopg2.errors.UndefinedColumn: column music_track.artist_id does not exist -LINE 1: ..."."title", "music_track"."base_run_time_seconds", "music_tra... -^ -HINT: Perhaps you meant to reference the column "music_track.artist_fk_id". -#+end_src - -**** scrobbles.tasks.import_from_webdav_all_users -#+begin_src bash -File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/importers/webdav.py", line 166, in scan_webdav_for_koreader -if last_import and last_import.webdav_etag and remote_etag: -^^^^^^^^^^^^^^^^^^^^^^^ -AttributeError: 'KoReaderImport' object has no attribute 'webdav_etag' -#+end_src - -**** scrobbles.tasks.process_bgstats_import -#+begin_src bash - -Traceback (most recent call last): -File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute -return self.cursor.execute(sql, params) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -psycopg2.errors.NotNullViolation: null value in column "visibility" of relation "scrobbles_scrobble" violates not-null constraint -DETAIL: Failing row contains (374463, 2026-06-11 13:27:06.528319+00, 2026-06-11 13:27:06.52834+00, 2026-06-11 13:17:34+00, 180, f, f, BG Stats, 1, null, t, {"players": [{"new": false, "win": false, "rank": 0, "role": "",..., null, null, null, 8e73ceec-b731-4623-9637-712bbf9f76ce, null, null, null, null, , null, BoardGame, 324, null, null, America/New_York, null, null, , null, null, , null, null, null, null, null, null, null, null, null, null, null). -#+end_src - -* Version 50.1 [1/1] -** DONE [#B] Fix bug in charts where only #1 is displayed :charts:templates: -:PROPERTIES: -:ID: 7136dffb-e6b7-184b-48ac-bb09bae0b0f0 -:END: - -* Version 50.0 [2/2] -** DONE [#A] Allow updating all a user's scrobble visibility at once :scrobbles:sharing:feature: -:PROPERTIES: -:ID: 9ed2ec65-bf69-4300-965c-6a7d3ef7ea03 -:END: - -*** Description - -We now have the ability to share or unshare scrobbles and create private links. -We should add a toggle in the user's settings that will bulk make all their -scrobbles public or private, so that a user can either share everything, or lock -their account down. - -This should not affect scrobbles that are in the "Shared" visibility state. - -And users should be able to also control whether all scrobbles of a specific -type are shared or not. Maybe this could be a JSONField in profile that contains -a media_type key with a visibility type for a value, and if it's not present, -sharing defaults to private? - -Additionally, users's should have links in their settings to see what scrobbles -are either public, shared or private. Probably this could be done with a -?visbility=<> filter on the /scrobbles/ page. - -*** Changes - -- Added `media_type_visibility` JSONField to UserProfile (migration 0038) -- Created `BulkVisibilityView` at `/settings/visibility/` with: - - Radio toggle to make all non-shared scrobbles Public or Private - - Per-media-type dropdown for each of the 20 media types (inherit/public/shared/private) -- Created `BulkVisibilityForm` with dynamic media_type fields -- Created `profiles/visibility_settings.html` template with visibility stats + filter links -- Added link from main settings page to visibility settings -- Added `?visibility=` filter support to `ScrobbleListView` (public/shared/private) -- Added filter indicator to `scrobble_all_list.html` -- Updated `Scrobble.create()` to check `user.profile.media_type_visibility` for media-type-specific defaults before falling back to PRIVATE - - - -** DONE [#A] Replace columsn of Top Artists, Tracks and Series with Maloja widget :templates:charts: -:PROPERTIES: -:ID: 3946afb1-932c-46fe-a188-f4c9add1a491 -:END: - - -*** Description - -The tables are fine, but Maloja widgets are better. We should drop the top track table, add top albums -and replace top artists and top tv series with the Maloja style widgets. - - -* Version 49.1 [1/1] -** DONE [#A] Fix bug with missing default visbility for scrobbles :bug:scrobbles:sharing: -:PROPERTIES: -:ID: 20843992-6453-9a9a-cde6-2c2b6677db23 -:END: - -*** Description - -We can't scrobble anything now because visbility is not null, but has no default -value. - -*** Notes -- Note taken on [2026-06-09 Tue 13:14] - The full stack trace: - #+begin_src sh - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/models.py", line 1430, in create_or_update - elif "log" in scrobble_data.keys() and scrobble.log: - ^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/models.py", line 1583, in create - ) - - File "/usr/local/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method - return getattr(self.get_queryset(), name)(*args, **kwargs) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django/db/models/query.py", line 658, in create - obj.save(force_insert=True, using=self.db) - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/models.py", line 870, in save - if self.media_obj: - ^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django_extensions/db/models.py", line 22, in save - super().save(**kwargs) - File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 814, in save - self.save_base( - File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 877, in save_base - updated = self._save_table( - ^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 1020, in _save_table - results = self._do_insert( - ^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django/db/models/base.py", line 1061, in _do_insert - return manager._insert( - ^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method - return getattr(self.get_queryset(), name)(*args, **kwargs) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django/db/models/query.py", line 1805, in _insert - return query.get_compiler(using=using).execute_sql(returning_fields) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1822, in execute_sql - cursor.execute(sql, params) - File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 67, in execute - return self._execute_with_wrappers( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers - return executor(sql, params, many, context) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute - with self.db.wrap_database_errors: - File "/usr/local/lib/python3.11/site-packages/django/db/utils.py", line 91, in __exit__ - raise dj_exc_value.with_traceback(traceback) from exc_value - File "/usr/local/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute - return self.cursor.execute(sql, params) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - django.db.utils.IntegrityError: null value in column "visibility" of relation "scrobbles_scrobble" violates not-null constraint - DETAIL: Failing row contains (373813, 2026-06-09 17:13:38.11355+00, 2026-06-09 17:13:38.113566+00, 2026-06-09 17:13:36+00, 0, f, f, Todoist, 1, null, t, {"title": "Animal chores", "labels": ["chore", "farm"], "todoist..., null, null, null, 68680dbf-f9a9-476c-b1c7-adbd231bbab6, null, null, null, null, , null, Task, null, null, null, America/New_York, null, null, , null, null, , 72, null, null, null, null, null, null, null, null, null, null). - #+end_src - -* Version 49.0 [1/1] -** DONE [#A] Fix broken tests with new sharing and add tests :scrobbles:sharing:tests: -:PROPERTIES: -:ID: 10ecd169-eaee-8554-d4ee-f1d34bfad99f -:END: - -* Version 48.3 [1/1] -** DONE [#A] Fix bug in missing sqids dep :dependencies:project: -:PROPERTIES: -:ID: 0b619837-729a-cd74-7a97-6fa2a148b27d -:END: - -* Version 48.2 [1/1] -** DONE [#A] Lock down scrobbles and use sqids to share them :feature:sharing:scrobbles: -:PROPERTIES: -:ID: a6e869f7-8012-7e83-8f68-d0a0ed4c3c6a -:END: - -*** Description - -Currently all scrobbles are public. Anyone with the uuid can view any other -scrobbles. We should use SQIDs to allow shareable links to scrobbles and then -make all scrobbles hidden by default. - - -* Version 48.1 [2/2] -** DONE [#A] Generate a report of tracks with mistmatched metadata :music:tracks:metadata: -:PROPERTIES: -:ID: 684b8cd2-a3c1-4995-ba9e-7abdb02c37f2 -:END: - -*** Description - -We should have a management command that outputs a CSV file of track IDs where -the log["raw_data"]["Artist"] (for Jellyfin) or log["raw_data"]["artist"] -(mopidy) value does not match the Track.artists names. - -And we should see the same thing for albums (log["raw_data"]["Album"] or -log["raw_data]["album"]). - -It should output the fields "track_id", "track_artist_name", "track_album_name", -"raw_artist", "raw_album", "source" - -Where source is either Jellyfin or Mopidy based on the keys. - -Put the file /tmp/metadata-report.csv by default and overwrite exsiting reports. - -The command should also accept a file-path to overide this default. - - -** DONE [#A] Date parsing failing in eBird imports :birds:ebird:importers: -:PROPERTIES: -:ID: 72e0254f-39d8-4843-9857-623e0362d77e -:END: - -*** Description - -On line 45 in the apps/birds/importer.py file, the import is thorowing this -error: - -#+begin_src python -ValueError: time data 'Jun 7, 2026, 5:15 PM' does not match format '%B %d, %Y %I:%M %p' -#+end_src - -Historically other files starting on May 24 worked, so I suspect this is a -problem of the date formatter expecting Long month names and zero-padded days -and the only sample we had was a three-letter month (May) and days with two -digites(24 through the 31) - -We should also add a "error_log" to the importers so that errors tha occur are -surfaced, even when 0 successful files were processed. And we should make sure -all importers do this as well. - - - -* Version 48.0 [2/2] -** DONE [#B] Show team or player images on sport detail and scrobble detail :sports:templates: -:PROPERTIES: -:ID: 68c17383-ee6e-4b5f-b3f5-1b637a0a3ea8 -:END: - -*** Description - -On the sport event detail page, we should show the images of the teams or -players invovled. - -Also, those images for the sport event should be shown on the scrobble detail -page for sport event scrobble details. - -** DONE [#B] Add fix_metadta method to Video instances :videos:metadata: -:PROPERTIES: -:ID: 9df5404d-1b60-4eee-b7cf-1f7e6dfade65 -:END: - -*** Description - -Turns out we don't have a fix_metadata method for videos. We should add that using -the basic logic from find_or_create on the Video model. - - -* Version 47.2 [1/1] -** DONE [#B] Add OMDB source as backup when TMDB returns nothing :videos:metadata:imdb: -:PROPERTIES: -:ID: 20195445-7fdd-49be-9767-103b12da0bfb -:END: - -*** Description - -TMDb works great for most cases. There are some edge cases, though where it does -not import videos, when TV shows are split up differently in TMDb than in IMDB. -One example I stumbled on is the 2020 reboot of Animaniacs. TMDb splits the -epiodes up in three parts, while they were always broadcast three-in-one, and -that's how IMDB lists them. Thus, the IMDB ID means nothing, and the videos end -up unenriched. - - -* Version 47.1 [1/1] -** DONE [#A] Untangle the sports migrations errors :sports:bug:migrations: -:PROPERTIES: -:ID: 4d50ca2e-f45b-dde8-e3c9-cd84f353b349 -:END: - - -* Version 47.0 [1/1] -** DONE [#B] Change sports scrobbling a bit :feature:sports:scrobbles: -:PROPERTIES: -:ID: cd27d683-c847-4251-b3d1-8243f45c01ca -:END: - -*** Description - -Currently, the way we scrobble sports means that basically the same event will -never be scrobbled again. I will likely never watch the 2025 Monaco Grand Prix -again, but I will watch the Monaco Grand Prix again. But I also wont watch one -specific game between Arsenal and Man City twice, but I may watch those two -teams play multiple times. - -What if instead of scrobbling a specific sports event on a specific date, we -make the unique Scrobblable item the players or teams in the event? - -That would not work for races where the unique item would have to be the name of -the event. - -Maybe that means SportEvent is too generic, and we'd need the event type to be -scrobble items. - -A race, the Indy 500 or Coke 600, or Boston Marathon would be scrobblable, while -for games, the teams would be unique, so a game between Arsenal and Man City -would be unique (with extra logdata context for who's home and who's away, and -the location, could even have the weather per scrobble). - -And finally, for Tennis, the title would be the round of the event, Roland -Garros Women's Semifinal, US Open Men's Final, Miami Invitational Round of 32, -with two players, or two teams and a start datetime, which is similar to what we -have now. The round becomes not a foreign key, but just a string, and we'd need -a FK to an organizer field which would replace league, and would be like "ATP -Tour" or "PGA Tour". Season would also need to be a string, and would be -something like: 2026 or 2024-2025. - -Examples: - - Super Bowl - - Sochaux v Concarneau - - French Open Final - - Carlos Alcaraz v Jannik Sinner - -We'd also want a script to reorganize existing sports events and move scrobbles -to the right place as best as we're able, and to flag sportsevents and scrobbles -that could not automatically be migrated with a unique tag like -"migration-failed" - -Ultimately I think what we need is to greatly simplify the SportEvent to be just a placeholder -for a sport event type for a given league, then each scrobble holds the details of teams, players -start, thesportsdb_id, round and season. - -Thus, I've already simplified that model, but what we need is a migration script that will move -existing complex SportEvent instances into very basic ones, and updating any scrobbles for those -events with a new SportEventLogData structure with all the specific details in it. We also need to -move the obj.round.season.league into the FK for the given event. - - - -* Version 46.0 [1/1] -** DONE [#C] Add sentiment parsing for Scrobbles with notes :scrobbles:sentiment: -:PROPERTIES: -:ID: 37781d6a-f3b0-48b2-bf98-33c2c791cf85 -:END: - -*** Description - -Not sure how useful this would be, but I wonder if we can add a `sentiment` JSONField -on each scrobble that can store the output of VADER over the notes in a scrobble with notes. - -I'm not sure that the value prop here is worth the storage and processing time. - -But if we do add it, it should be a process that scans for scrobbles with both notes and no -sentiment field value (unless --overwrite is used) and just run periodically. - - -* Version 45.1 [1/1] -** DONE [#B] Mopidy favorites or monthly playlist adds should look at all scrobbles :bug:mopidy:favorites:tracks: -:PROPERTIES: -:ID: 0be7d11e-e268-2fd5-836a-e5b4d210e0fa -:END: - -*** Description - -When favoriting a track and trying to add it to the Moidy favorite playlist, it -sometimes happens that one scrobble did not come from Mopidy, but an earlier or -later one did. - -Can we scan all the scrobbles of the track for a given user to see if any have -`mopidy_uri` in the log and if so, use that to send along to Mopidy? - - -* Version 45.0 [1/1] -** DONE [#B] Add ability to add mopidy tracks to Monthly playlists :feature:favorites:tracks: -:PROPERTIES: -:ID: c872ff0a-e71f-415f-b5a6-e62ea9634d14 -:END: - -*** Description - -Now that we can favorite a mopidy track and have it added to a Favorites playlist, it would -be great if we could also populate a monthly_mopidy_playlist_pattern in a user profile and, -if configured, you could press "Add to monthly playlist" button on a given track that has -a mopidy_uri in it's log, and it would be added to the playlist. - -The patterns would be based on traditional Django date formatting patterns: https://gregbrown.co/code/date-format - -So "Y m F" would yield "2026 05 May" if the link is clicked in May of 2026. - - -* Version 44.0 [1/1] -** DONE [#B] Add favorite feature for scrobbles :feature:favorites:scrobbles: -:PROPERTIES: -:ID: 2780ae5f-fe23-49a5-8b33-d19e7f3e8ec6 -:END: - -*** Description - -Would be great to have a FavoriteMedia data model that would accept any media_type ID and a user_id -marking that media as a favorite for that user. - -Additionally, for tracks, we should add the ability to set a "favorites_mopidy_playlist" in a user profile -and if populated, and a track media type is favorited, and the track has a mopidy_uri value in a scrobble log, -send a POST to the mopidy server RPC endpoint for the favorite playlist and add the track. - - - -* Version 43.0 [5/5] -** DONE [#B] Can we show a graph of all past Weigh-in tasks :scale:tasks:graphs:javascript: -:PROPERTIES: -:ID: ae499d87-03bf-4e48-9b2c-1a421a46af11 -:END: - -*** Description - -I wonder if, as a special type of task, Weigh-in's could show a graph of the -metrics that are stored against all the past weigh-ins? - -The graph would contain all Weigh-in scrobbles for that user, no matter which -date is being viewed, and the highlighted value on the graph would be the date -being viewed. - -Probably could use something like chart.js although maybe that's too heavy? - -And can we have each metric overlayed on the same graph? - -** DONE [#B] When viewing scrobbles by tag, sum the total time :scrobbles:tags: -:PROPERTIES: -:ID: d51f23df-c2c5-4e1a-b000-67c89032af02 -:END: - -*** Description - -On scrobbles filtered by tags, we should see a sum of the time spent doing those tasks, in a human -readable format like "X days, X hours, X minutes and X seconds" - -** DONE [#A] Orgmode tasks are not updated if in progress :tasks:orgmode:bug: -:PROPERTIES: -:ID: 7dcebb2c-7c4c-4ac5-bee6-c2e36c3811f9 -:END: - -*** Description - -Currently if you POST to the orgmode webhook with a task that's already in progress, the request just stops there. - -We should add logic where if the task is in-progress, instead of doing nothing, -it checks the webhook payload against the in-progress tasks and updates the -description of the scrobble.log with the incoming task description if it's -different. And the same for comments. If a comment (by timestamp key) is -different in the webhook than what's in the scrobble.log, update the comment in -the scrobble.log - -** DONE [#A] Ignore tag 'inprogress' for Tasks :bug:tasks:tags: -:PROPERTIES: -:ID: cd37c1ec-e2fc-b93c-daf8-6b329712c3f1 -:END: - -*** Description - -When scrobbling tasks from Todoist, the tag `inprogress` is always in the -payload, because that's how we parse tasks starting from the Todoist webhooks. - -But we don't really need anything tagged as `inprogress` Can we ignore this tag -when applying tags to Task scrobbles coming from Todoist?` - - - -** DONE [#A] Deploys are now throwing an unknown version error :bug:tooling:releases: -:PROPERTIES: -:ID: 3870f9d3-b5ed-4b87-9e8c-9bf905bfb766 -:END: - -*** Description - -Almost everything is working, but for some reason `__version__` does not seem to -exist. - -#+begin_src sh -out: Installing collected packages: vrobbler -out: Successfully installed vrobbler-42.0 -err: WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv -err: Traceback (most recent call last): -err: File "", line 1, in -err: AttributeError: module 'vrobbler' has no attribute '__version__' -2026/06/04 17:18:15 Process exited with status 1 -failed to remove container: Error response from daemon: removal of container c8ac64bee9b6bf5978d2c16f299e5ac271d8bbf7192b7a4023c3712bc2444f8b is already in progress - ❌ Failure - Main Install wheel and restart services -exit with `FAILURE`: 1 -#+end_src - - -* Version 42.0 [1/1] -** DONE [#B] Add ability to add track to current Mopidy queue :feature:mopidy:tracks: -:PROPERTIES: -:ID: 79d5b580-4ea6-461b-4c6c-2c950d8b3e4c -:END: - - -* Version 41.0 [5/5] -** DONE [#B] For any scrobble detail page with notes display them better :templates:notes:scrobbles: -:PROPERTIES: -:ID: c0dcf9da-227f-4a22-bcd9-9d46053607d9 -:END: - -*** Description - -Currently notes are displayed as little post-it notes. This is cute, but not terribly useful. - -We should update note rendering to be a simple newest to oldest display in a -single column with the timestamp has a small header, and the content rendered as -markdown with a small bar or horizontal divider marking them from the next note. - -** DONE [#A] Imports should send notifications :feature:notifications:imports: -:PROPERTIES: -:ID: 6f78f8d5-ecaa-4d8a-a666-ae4e27653191 -:END: - -*** Description - -Currently importing board games sends out a ntfy message when a scrobble is -created. - -We should do the same thing for other import types; namely: gpx, ebird, and scale. - -** DONE [#A] Board game imports send duplicate ntfy message :bug:notifications:boardgames: -:PROPERTIES: -:ID: 8f067432-0399-4b79-9e93-727edcccedbd -:END: - -*** Description - -When a board game scrobble is created via a bgstats import, ntfy messages are -sent. - -But right now they are duplicated (two are sent at the same time). Can we review -the code to see why this is happening and fix it? - -** DONE [#A] Too many geolocation notifications go out :bug:notifications:geolocations: -:PROPERTIES: -:ID: 6357ad7a-fe4e-49dd-a063-55d87e459c17 -:END: - -*** Description - -Currently ntfy gets overwhelemed when there's more than a hundred or so messages left in a queue on a client. - -It would be nice if we could not spam ntfy, and this is especially true with Geolocations, where we really -don't need to alert folks unless they have a named Geolocation (has a title). Can we adjust the ntfy -sending for Geolocations to only send if the scrobbled location has a title? - - -** DONE [#C] Fix bug where Weigh-in imports do not set title :bug:tasks:scale: -:PROPERTIES: -:ID: 622e354a-8e66-4ecd-9e1c-a53f0a2ec362 -:END: - -*** Description - -Currently when we import a scale CSV row and create data, the title is left -blank which makes it look funny in a list view. Let's save the weight as the -title of the Weigh-in task. - - -* Version 40.2 [1/1] -** DONE [#A] Try fixing deploy bugs again :tooling:releases:bug: -:PROPERTIES: -:ID: 15894943-be1d-200f-8400-a136770ad9d2 -:END: - - -* Version 40.1 [2/2] -** DONE [#A] Releases are still broken :bug:releases:tooling: -:PROPERTIES: -:ID: bca37a18-afa2-4ddd-a11b-ef0555f38bc9 -:END: - -*** Description - -Deploys are still broken, even with them being pulled apart and run separately. - -We need to address the way the commit ends up stashed in the codebase. - -** DONE [#C] Fix bug on chart pages where trail titles missing :bug:trails:charts: -:PROPERTIES: -:ID: 21075430-8a93-4e59-9a02-479315960ae6 -:END: - -*** Description - -When trails are rendered on the chart views, there are no titles, which means we don't see -anything except the ranking number. - - -* Version 40.0 [2/2] -** DONE [#A] Fix error in org-mode task sync :emacs:orgmode:tasks:bug: -:PROPERTIES: -:ID: 03256d2a-48aa-4be7-aeb3-fa1cfddc86bf -:END: - -*** Description -#+begin_src python - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/tasks/webhooks.py", line 236, in post - emacs_scrobble_update_task( - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/scrobblers.py", line 844, in emacs_scrobble_update_task - for note in emacs_notes: -TypeError: 'NoneType' object is not iterable -#+end_src -** DONE [#B] Adjust how similar artists are shown :feature:templates:artists:music: -:PROPERTIES: -:ID: 2a081620-a0a2-4851-a7cf-4043f9c2ee31 -:END: - -*** Description - -Currently we show the top 10 similar artists on the Artist detail page linked to -the artist detail page on Vrobbler. - -First off, this is very slow. We should look into speeding up the rendering of -the similar artists widget. - -Second, the artist name in the similar artist list should be a link to the -Vrobbler artist detail page, but there should also be a [musicbrainz] link next -to it, that links out to the musicbrainz page whether we have the artist in the -Vrobbler database or not. - - - -* Version 39.3 [2/2] -** DONE [#A] Issue found when doing a release :bug:tooling:release:cicd: -:PROPERTIES: -:ID: a8fc3ec9-74ec-4190-8ac2-62cd8a33e828 -:END: - -*** Description -#+begin_src sh -err: ERROR: Cannot install vrobbler 0.16.1 (from /var/lib/vrobbler/dist/vrobbler-0.16.1-py3-none-any.whl) and vrobbler 38.0 (from /var/lib/vrobbler/dist/vrobbler-38.0-py3-none-any.whl) because these package versions have conflicting dependencies. -out: The conflict is caused by: -out: The user requested vrobbler 0.16.1 (from /var/lib/vrobbler/dist/vrobbler-0.16.1-py3-none-any.whl) -out: The user requested vrobbler 38.0 (from /var/lib/vrobbler/dist/vrobbler-38.0-py3-none-any.whl) -out: To fix this you could try to: -out: 1. loosen the range of package versions you've specified -out: 2. remove package versions to allow pip attempt to solve the dependency conflict -err: ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/latest/topics/dependency-resolution/#dealing-with-dependency-conflicts -2026/06/01 14:15:00 Process exited with status 1 -failed to remove container: Error response from daemon: removal of container 3fe0eaf032c5518aca4ab71734b52bda7c54ed406136b82136ab7155bf5ff3c1 is already in progress -#+end_src - -** DONE [#A] Fix deploy actions running twice :bug:tooling:cicd: -:PROPERTIES: -:ID: aa56f2a6-2b61-4ddf-9e27-9eadcddf8412 -:END: - -*** Description - -Turns out we're now running the build-deploy action twice, once on branch push -and once on tag push. - -We should do builds on each push and build and deploys only when a new tag is -detected. - - -* Version 39.2 [2/2] -** DONE [#B] Releases do not pin commit to the repo for display :bug:tooling:releases: -:PROPERTIES: -:ID: 2a9f2ff5-2642-47ab-ba1d-e41825411713 -:END: - -*** Description - -Somewhere in implementing the justfile release flow, we lost the capture of the -latest commit in the relesae flow so the footer now always says: vXX.x (unknown) - -It should have the first bit of the commit in the parens at the end. - -** DONE [#B] Fix the way timestamps are stored for notes on tasks :bug:scrobbles:tasks: -:PROPERTIES: -:ID: 32973bb3-079b-8cdf-6495-82f8ae907299 -:END: - -*** Description -:PROPERTIES: -:ID: d833a3df-6eb2-36bb-1863-e438e5d36151 -:END: - -Turns out Todoist uses a human-readable timestamp format for comments. We should -adapt that for use from org-mode as well. - -Format should be: "%Y-%m-%dT%H:%M:%S.%fZ" - - -* Version 39.1 [1/1] -** DONE [#A] Fix bug in tests for notes saving :bug:scrobbles:forms: -:PROPERTIES: -:ID: 68a011b2-bb6f-3ba8-2312-5947c41db9ac -:END: - - -* Version 39.0 [3/3] -** DONE [#B] Clean up org-mode tasks metadata :bug:tasks:metadata: -:PROPERTIES: -:ID: 0c762d09-fc69-4e75-be40-7eaaf04f178e -:END: - -*** Description - -Org-mode tasks have a `Description` subheader, which should populate the -"Description" of a task. - -The title should come from the actual TODO content, with tags coming from the -tags in colons after the task. This behaviour should already work. - -Next, any comments should go under a `Comments` subheader. Under these should be a list tag like: - -"Note taken on [2026-05-31 Sun 20:03]" - -Which declares it's a note with a timestamp like "YYYY-MM-DD d HH:MM". - -When scrobbling a task from org-mode, the description should always be copied to -the scrobble's log["description"]. - -Any comments that exist on the task when the scrobble is first created, should -be ignored. - -Any comments on a task that is updated (a re-POST'd while the task is in -progress, should add a comment in the log["notes"], a dictionary, keyed off the -timestamp the note string. If a note for that datetime already exists, the -content should be replaced with the new comment. - -Additionally, when a task is re-POST'd while the task is in progress, the -description should also be compared, and if different, the newest description -should be used. - -Note, the biggest change for this flow is making sure comments that already -exist are not added to any new tasks and that both comments and descriptions are -added or updated if the new values are different than what is already in the -currently in-progress task. - -If the task is completed, don't touch it. - -*** Comments -- Note taken on [2026-05-31 Sun 20:03] - -** DONE [#A] Actually push branches up and add a just command to do it :release:justfile:tooling: -:PROPERTIES: -:ID: 50aa5daa-a802-6aa9-38a3-218b7a9d4b34 -:END: -** DONE [#A] Try to fix deploy failing with bad release plan :release:tooling:pyproject: -:PROPERTIES: -:ID: 63dc633c-4382-e6a5-e663-b01871ce86ce -:END: - - -* Version 38.0 [38/38] -** DONE [#A] Fix release flow to be easier to trigger :pyproject:release:tooling: -:PROPERTIES: -:ID: e321f8a3-26ea-b316-8493-294b50c0b516 -:END: - -*** Description - -We should have a command like `just relesae ` that can cut new -releases on command by updating the PROJECT.org file with a release header for -all DONE tasks and creating the appropriate release commits and tags based on -either the new release version type. - -It should also update the pyproject file. - -** DONE [#A] Move imported retroarch lrtl files to processed/ directory on WebDAV :webdav:retroarch:importers: -:PROPERTIES: -:ID: 881b5af6-e96f-4e71-9e66-2d30824e5504 -:END: - - - File: ~vrobbler/apps/scrobbles/importers/webdav.py~ (line 439) - - Same pattern as the GPX importer: after importing a =.csv= file from - WebDAV, move it to =var/retroarch/processed/= with a timestamp appended. - -** DONE [#A] Add listenbrainz support for similar tracks :feature:music:metadata: -:PROPERTIES: -:ID: a3abba14-7603-db51-58e1-1941a47fa35e -:END: -** DONE [#B] Consolidate albums in the same musicbrainz_releasegroup_id :music:albums:metadata: -:PROPERTIES: -:ID: 6b331bdb-1516-bb86-7cb3-8cb9e3de59ce -:END: - -*** Description - -When we look up albums, we should check if one already exists with the same -musicbrainz_releasegroup_id and prefer that one, rather than creating a new -album. - -Also, we should create a data migration to clean up albums with matching -musicbrainz_releasegroup_id fields by consolidating tracks around the first -album found. - -Whether the track was enriched by musicbrainz or not, the track should get -tagged with `musicbrainz_enriched` or `musicbrainz_notfound` - -** DONE [#A] Clean up metadata on music tracks :music:tracks:metadata:musicbrainz: -:PROPERTIES: -:ID: 3a007bd7-b901-43cb-892e-102d39fffc85 -:END: - -*** Description - -There's a over 3K tracks without a musicbrainz_id and almost 30K without a -base_run_time_seconds. We should have a clean up script that can run through the -ones missing musicbrainz_ids and see about getting metadata from musicbrainz and -for the 30K without base_run_time_seconds, check with their musicbrainz_ids to -see if we can look up the track length, and if not, tag the track with -"missing-metadata" tag. - -Also, if the tracks without musicbrainz_ids actually are not in the MB database, -we should tag those with "not-in-musicbrainz" - -And a big part of this work will probably involve checking for "Various Artists" -tracks where the track erroneously got set with a generic artist. In those -cases, we should try just looking up by track title. - -** DONE [#B] Make artists_m2m field source of artist truth for albums :music:bug:albums: -:PROPERTIES: -:ID: 05a24455-0e71-45ef-ac6e-c1b7b843047d -:END: - -*** Description - -Albums have an FK for album_artist, but like artists, the M2M should be the -source of truth. We should migrate all uses of album_artist to an `artist` -property on the Album model and use a data migration to populate artists with -the album_artist value. - -** DONE [#A] Fix various artist album problem with Superwolves (track with multiple artists) :vrobbler:project:music:bug:artists: -:PROPERTIES: -:ID: 590bc038-745f-710b-8272-4d8a3d2efa01 -:END: - -*** Description -We have an issue with tracks where there are two artists, like `Matt Sweeney & -Bonnie "Prince" Billy` - -I think we need to allow creating a single artist with both names, so the Artist -is the full name. That said, the ampersand is usually used to split feature -artsits (I think) so I'm not sure how this would work reliably. - -Also, it's possible musicbrainz does not have dual artist listings. So it's -possible the longer term solution is to allow multiple artists per track the way -we now allow tracks to be on multiple albums. We could deprecate the Artist FK -or at least make it optional, and then require a M2M between Track and Artist. - -Then this one would be a Track by both `Matt Sweeney` and `Bonnie "Prince" -Billy` - -** DONE [#A] Move imported eBird CSV files to processed/ directory on WebDAV :webdav:ebird:importers: -:PROPERTIES: -:ID: 445e1253-d353-4b55-b1d8-39d0a0dcdd34 -:END: - - - File: ~vrobbler/apps/scrobbles/importers/webdav.py~ (line 439) - - Same pattern as the GPX importer: after importing a =.csv= file from - WebDAV, move it to =var/ebird/processed/= with a timestamp appended. - -** DONE [#A] Move imported Board Game CSV files to processed/ directory on WebDAV :webdav:boardgames:importers: -:PROPERTIES: -:ID: a3f8d30c-d2c3-4ee7-b062-f0f16bd9b0b4 -:END: - - - File: ~vrobbler/apps/scrobbles/importers/webdav.py~ (line 496) - - Same pattern as the GPX importer: after importing a =.csv= file from - WebDAV, move it to =var/bgstats/processed/= with a timestamp appended. - -** DONE [#A] Move imported Scale CSV files to processed/ directory on WebDAV :webdav:scale:importers: -:PROPERTIES: -:ID: 1a0de363-d1ea-466e-9966-e24941a6180b -:END: - - File: ~vrobbler/apps/scrobbles/importers/webdav.py~ (line 496) - - Same pattern as the GPX importer: after importing a =.csv= file from - WebDAV, move it to =var/scale/processed/= with a timestamp appended. - -** DONE [#A] Allow special parameter to re-import already processed GPX files :imports:gpx: -:PROPERTIES: -:ID: 166c0809-c11a-4d02-8071-7a69dcb36e64 -:END: - -*** Description - -Now that we stash imported GPX files in the processed/ subdirectory on import, -it would be nice to have a flag like --include-processed on the webdav importer -that would import files both in the root of gpx/ and also in the processed -directory. This would aide testing imports in staging quickly without constantly -moving files back and forth. - -** DONE [#A] Move imported GPX files to processed/ directory on WebDAV :webdav:gpx:importers: -:PROPERTIES: -:ID: db2b02fc-817c-4c39-bd51-13f9b77c7888 -:END: - - File: ~vrobbler/apps/scrobbles/importers/webdav.py~ (line 198) - - After importing a GPX/FIT file from WebDAV, move it to a =processed/= - subdirectory with a timestamp appended. This eliminates the DB lookup for - already-imported filenames — any file present in the top-level directory - is new. Also makes manual re-imports easy (just move a file back). - -** DONE [#A] Add CSS Grid calendar view for scrobbles :vrobbler:personal:project:templates:feature: -:PROPERTIES: -:ID: be915acf-d803-466a-8770-823819ebf2a9 -:END: - -*** Description - -Calendar view at /scrobbles/calendar/ showing select media types (Tasks, -Birding, Food, Trails, VideoGames, Books) in a CSS Grid month layout. - -- Emoji badges per scrobble on each day cell, hover reveals title -- Click emoji to navigate to scrobble detail -- Prev/Next month navigation -- Based on the CodePen CSS Grid calendar pattern: - https://codepen.io/oliviale/pen/QYqybo - -** DONE [#C] Come up with a possible flow using WebDAV and super-productivity for tasks :personal:feature:project:vrobbler:tasks: -:PROPERTIES: -:ID: dd751edf-6192-2ed0-cd85-905f423f82b0 -:END: -** DONE [#B] Fix PuzzleLogData has no attribute form :vrobbler:puzzles:personal:project:logdata: -:PROPERTIES: -:ID: cb8e697a-9fc2-c463-5d4d-fa15d3e833a0 -:END: -** DONE [#B] Add PuzzleLogData class with with_people and completed :vrobbler:feature:puzzles:logdata:personal:project: -:PROPERTIES: -:ID: 1b38c8a8-86d3-b73d-c584-dfb0497970dc -:END: -** DONE Add weather lookup to the mood check-in flow :vrobbler:project:moods:feature:checkin: -:PROPERTIES: -:ID: 175678e2-bfe1-0833-f2a9-f03b9323db78 -:END: -<2026-05-20 Wed> -** DONE Add importing of openScale CSV files to Tasks :vrobbler:project:personal:tasks:openscale: -:PROPERTIES: -:ID: 344d1f5d-dda6-a1fb-da53-e00eebdd2262 -:END: -** DONE Add ability to track Birding sessions via BirdingLocation scrobbles :vrobbler:project:birds:feature: -:PROPERTIES: -:ID: 8200ce29-a691-5cf6-c11a-c77e3d8b64c6 -:END: -** DONE List only the last 20 scrobbles per category on the home page :vrobbler:project:scrobbles:templates: -:PROPERTIES: -:ID: b56402b9-5542-a10c-6c13-e12f56f2a2d1 -:END: -** DONE Fix display of notes so they look like stickies :vrobbler:project:personal:notes:scrobbles: -:PROPERTIES: -:ID: 0ace8814-e96e-fa54-28d1-c57dcb508f1e -:END: -** DONE Add searching to scrobbles :vrobbler:project:personal:search:scrobbles: -:PROPERTIES: -:ID: b4690c76-d741-4320-20e8-c003251fe035 -:END: -** DONE Fix uniqueness of imdb_id messing up youtube videos :vrobbler:project:bug:videos: -:PROPERTIES: -:ID: 610b8c4c-abf1-8630-a4fd-08d9939da9f5 -:END: - -Turns out you can't make imdb_id unique by itself or you never get to create -youtube videos. Rather, we should make imdb_id and youtube_id unique together so -imdb_id can be empty for every youtube_id and vice versa. - -** DONE Fix genearting chart records :vrobbler:bug:personal:project:chartrecords: -:PROPERTIES: -:ID: e749d762-44ac-dd7e-017e-2423016737ff -:END: - -** DONE [#A] Save raw scrobble request data to every scrobble log :vrobbler:personal:feature:scrobbles: -:PROPERTIES: -:ID: b4686db4-1319-399c-9b84-f0b1036c6815 -:END: - -The idea here is that no matter where the data comes from, we should just save -it in the scrobble for posterity, so we can always in some form recover the -original intent of the scrobble. - -It may also allow us to clean up junk scrobbles if the data was just horribly -wrong. - -** DONE [#B] Clean up follow up notifications for if you're still scrobbling :vrobbler:personal:project:beers:boardgames:notifications:feature: -:PROPERTIES: -:ID: 7937545f-2a62-4188-ad49-34f85fad0c25 -:END: - -- Note taken on [2025-09-30 Tue 09:32] - - I added this feature in a very rough way, but now we should add "Action" - headers so that we can either Finish or Cancel the associated scrobble: - - https://docs.ntfy.sh/publish/#send-http-request - -** DONE [#A] Fix lookup of music tracks from Musicbrainz :vrobbler:bug:tracks:music: -:PROPERTIES: -:ID: 2bc1cc0b-6c4e-bc3d-e7c3-47df364df206 -:END: - -Turns out we're not looking up music tracks properly, again. - -** DONE Check opencode about a way to present stats like movies per month :vrobbler:scrobbles:stats:personal:project: -:PROPERTIES: -:ID: eed39a0b-3aa7-dc65-9e03-a58fa401c8c6 -:END: -** DONE Fix bug in Jellyfin audio track playback :vrobbler:personal:project:bug:music:jellyfin: -:PROPERTIES: -:ID: 002bd234-4507-5313-fb59-2d3baa49b847 -:END: - -#+begin_src shell -ERROR django.request:241 log_response Internal Server Error: /webhook/jellyfin/ -Traceback (most recent call last): - File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner - response = get_response(request) - ^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response - response = wrapped_callback(request, *callback_args, **callback_kwargs) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django/views/decorators/csrf.py", line 56, in wrapper_view - return view_func(*args, **kwargs) - ^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django/views/decorators/csrf.py", line 56, in wrapper_view - return view_func(*args, **kwargs) - ^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django/views/generic/base.py", line 104, in view - return self.dispatch(request, *args, **kwargs) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 509, in dispatch - response = self.handle_exception(exc) - ^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 469, in handle_exception - self.raise_uncaught_exception(exc) - File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception - raise exc - File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 506, in dispatch - response = handler(request, *args, **kwargs) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/rest_framework/decorators.py", line 50, in handler - return func(*args, **kwargs) - ^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/views.py", line 494, in jellyfin_webhook - scrobble = jellyfin_scrobble_media(post_data, request.user.id) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/scrobblers.py", line 177, in jellyfin_scrobble_media - log["album_id"] = media_obj.album_id - ~~~^^^^^^^^^^^^ -TypeError: 'tuple' object does not support item assignment -#+end_src -** DONE [#B] Auto calc duration if no playback time seconds present :vrobbler:bug:scrobbles:personal:project: -:PROPERTIES: -:ID: e16228b2-b062-bd00-32e6-b2353e6406e9 -:END: -** DONE Fix bug in video find_or_create :vrobbler:personal:project:bug:videos: -:PROPERTIES: -:ID: 10e611a5-fbcf-7473-25f0-c9bad7b79ffd -:END: -The error: -#+begin_src shell - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/views.py", line 493, in jellyfin_webhook - scrobble = jellyfin_scrobble_media(post_data, request.user.id) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/scrobbles/scrobblers.py", line 149, in jellyfin_scrobble_media - media_obj = Video.find_or_create(imdb_id) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/videos/models.py", line 390, in find_or_create - return cls.get_from_imdb_id(source_id, overwrite) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/videos/models.py", line 374, in get_from_imdb_id - video.tv_series = Series.find_or_create( - ^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/videos/models.py", line 203, in find_or_create - vdict, _, cover, genres = lookup_video_from_imdb( - ^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/videos/sources/imdb.py", line 17, in lookup_video_from_imdb - imdb_result = imdb.get_title(imdb_id) - ^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/cinemagoerng/web.py", line 127, in get_title - data = _scrape(spec=spec, context=context, headers=headers) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/cinemagoerng/web.py", line 120, in _scrape - document = fetch(url, headers=request_headers) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/cinemagoerng/web.py", line 44, in fetch - with urlopen(request) as response: - ^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/urllib/request.py", line 216, in urlopen - return opener.open(url, data, timeout) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/urllib/request.py", line 525, in open - response = meth(req, response) - ^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/urllib/request.py", line 634, in http_response - response = self.parent.error( - ^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/urllib/request.py", line 563, in error - return self._call_chain(*args) - ^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/urllib/request.py", line 496, in _call_chain - result = func(*args) - ^^^^^^^^^^^ - File "/usr/local/lib/python3.11/urllib/request.py", line 643, in http_error_default - raise HTTPError(req.full_url, code, msg, hdrs, fp) -urllib.error.HTTPError: HTTP Error 500: Internal Server Error -#+end_src - -- Note taken on [2026-03-11 Wed 20:53] - - It turns out that every time a TV show was scrobbled were were hitting IMDB. - Woof. - -** DONE Update admin page to be easier to use :vrobbler:djadmin:project:personal: -:PROPERTIES: -:ID: 0779be1f-1ad7-ddc7-8861-d3c7c833cbe9 -:END: -** DONE Fix migrations and update repo :vrobbler:scrobbles:admin:personal:project: -:PROPERTIES: -:ID: ccbc2ef0-68bf-9ef7-dcaf-febab5ebefe2 -:END: -** DONE Add recipe parsing for food lookups :vrobbler:foods:project:feature:personal: -:PROPERTIES: -:ID: 86456c78-247b-fb63-7ae8-d6d17e7666b1 -:END: -** DONE [#A] Videos are scrobbling duplicates again :vrobbler:bug:videos:scrobbles: -:PROPERTIES: -:ID: a46884fe-7ef1-410a-316b-7ac6d7599331 -:END: -<2026-03-06 Fri> -** DONE Fix board games not saving BGG id on lookup :vrobbler:bug:boardgames: -:PROPERTIES: -:ID: 506c2965-51d6-6cb9-fc4f-4f0468d2d62f -:END: -** DONE Fix board game lookup with name like Unmatched Game System :vrobbler:bug:boardgames: -:PROPERTIES: -:ID: 57956723-b34a-37ad-fde9-44947cc9bb65 -:END: -** DONE [#A] Fix raw text webpage title not truncating to 254 chars :vrobbler:personal:bug:webpages: -:PROPERTIES: -:ID: 13fa0efd-2c3f-dd07-deb2-62882096feff -:END: - -- Note taken on [2025-09-30 Tue 09:33] - - This may have already been resolved ... need to just confirm it. - - -* Version 37.0 [4/4] -** DONE [#A] Tasks from org-mode should properly update notes and leave them out of the body :vrobbler:bug:tasks: -:PROPERTIES: -:ID: c8410001-dbb7-1536-bd89-9784189e058f -:END: -** DONE [#A] Allow scrobbling from the Food list page's start links :vrobbler:bug:food:scrobbling:personal:project: -:PROPERTIES: -:ID: 00f99f60-ac00-6cde-311d-c31f41a01353 -:END: -https://life.lab.unbl.ink/scrobble/e39779c8-62a5-46a6-bdef-fb7662810dc6/start/ -** DONE [#B] Food scrobbles should inherit calories from obj if missing :vrobbler:feature:food:personal:project: -:PROPERTIES: -:ID: 3322ff69-4252-db65-36b3-fae56c1b9327 -:END: -** DONE [#A] Puzzles (and all longplays) should have a "Completed?" column on their detail page :vrobbler:bug:puzzles:personal:project: -:PROPERTIES: -:ID: e3e49a9a-67d2-8ad8-1114-6f05effee9b7 -:END: - -* Version 36.0 [1/1] -** DONE [#A] Refactor how videos are scrobbled :vrobbler:vidoes:feature:personal:project: -:PROPERTIES: -:ID: 6034a11d-5376-994d-9a4b-e1640e258cfa -:END: - -* Version 35.0 [3/3] -** DONE [#B] Add youtube link in place of IMDB on video detail page :vrobbler:feature:videos:personal:project: -:PROPERTIES: -:ID: 84064bd6-2258-a4de-f048-b131db9465c9 -:END: -** DONE [#B] Add missing API lookups to resolve broken scrobbles endpoint :vrobbler:feature:api:scrobbles:personal:project: -:PROPERTIES: -:ID: 0f668a54-f587-3b17-353e-3a56969d3a82 -:END: -** DONE [#A] IMDB lookups are not working :vrobbler:bug:videos:personal:project: -:PROPERTIES: -:ID: d1ba1ca1-509b-13a9-1307-b2dc94a2eafe -:END: - -* Version 34.0 [4/4] -** DONE [#A] Use bgg-api for BoardGameGeek lookups :vrobbler:feature:boardgames:personal:project: -:PROPERTIES: -:ID: 738abb5a-c796-b16b-fe10-6e5639a0e10d -:END: -** DONE [#A] Add classmethod for metadata fetching to tracks :vrobbler:feature:music:personal:project: -:PROPERTIES: -:ID: bc4b45e5-4c65-13c5-ab7b-1937d3fbf5c2 -:END: - -- Note taken on [2025-10-29 Wed 21:44] - - Beyond a classmethod (which I think we have now), we need to update the flow of how we look up tracks. - - It's a hot mess right now where Various Artists walks over the actual artist, and we often hit MB when we don't have to. - -** DONE [#A] Fix views for TV series where next episode is now None :vrobbler:bug:personal:videos: -:PROPERTIES: -:ID: d7014ac4-cda6-0802-2cdf-8f66c6389fea -:END: - -#+begin_src python -ERROR django.request:241 log_response Internal Server Error: /series/c24100d1-da45-4abe-86bf-27cfce9b1f89/ -Traceback (most recent call last): - File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner - response = get_response(request) - ^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response - response = wrapped_callback(request, *callback_args, **callback_kwargs) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django/views/generic/base.py", line 104, in view - return self.dispatch(request, *args, **kwargs) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django/contrib/auth/mixins.py", line 73, in dispatch - return super().dispatch(request, *args, **kwargs) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django/views/generic/base.py", line 143, in dispatch - return handler(request, *args, **kwargs) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/django/views/generic/detail.py", line 109, in get - context = self.get_context_data(object=self.object) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "/usr/local/lib/python3.11/site-packages/vrobbler/apps/videos/views.py", line 33, in get_context_data - context_data["next_episode_id"] = "tt" + next_episode_id - ~~~~~^~~~~~~~~~~~~~~~~ -TypeError: can only concatenate str (not "NoneType") to str -#+end_src - -** DONE [#A] Emacs tasks are duplicating rather than updating :vrobbler:bug:tasks:emacs:personal:project: -:PROPERTIES: -:ID: e93efc25-7ce9-8ef2-662e-0a19dd0b29c9 -:END: - -- Note taken on [2025-10-29 Wed 16:38] - - Turns out I was misusing `orgmode` for the source of tasks when it shoulda been `Org-mode` - - A good lesson in using constants for things. - - -* Version 33.0 [3/3] -** DONE [#A] Fix bug where scrobble is_stale only uses seconds not total_seconds :vrobbler:bug:scrobbles:personal:project: -:PROPERTIES: -:ID: 7f6070ac-4f67-011d-ebd5-f3dc47da46ed -:END: -** DONE [#B] Fix duplicatged Read next issue for Comic books :vrobbler:bug:books:personal:project: -:PROPERTIES: -:ID: 97943040-1f03-b0b7-b0aa-123a783e4f7b -:END: -** DONE [#A] Add API authentication to BGG calls :vrobbler:bug:boardgames:personal:project: -:PROPERTIES: -:ID: 4955cc34-0882-50db-92f7-f36a95bf57a4 -:END: -<2025-10-28 Tue> - -* Version 32.0 [2/2] -** DONE [#B] Save path to reading source on book scrobbles and show it on the detail page :vrobbler:feature:books:personal:project: -:PROPERTIES: -:ID: f1ef3945-e6e4-66c1-b72e-3cede7a0f84a -:END: -** DONE [#B] Move comic resume URL to next page and check if it exists :vrobbler:feature:books:personal:project: -:PROPERTIES: -:ID: 9fe09567-11a3-7083-53c7-07458a9591d0 -:END: - -* Version 31.0 [3/3] -** DONE [#A] Stop comic book webpage scrobbles from overwriting old scrobbles :vrobbler:personal:bug:books:scrobbling: -:PROPERTIES: -:ID: 4b2ec068-a281-a88b-c31d-6248d6eb0aa0 -:END: -** DONE [#A] Add page calculation to manually scrobbled books :vrobbler:personal:feature:books:scrobbling: -:PROPERTIES: -:ID: b2e313b3-5c35-57e7-8933-627535baf34b -:END: -** DONE [#A] Fix bug in scrobbling comics where google fails :vrobbler:personal:bug:books:scrobbling: -:PROPERTIES: -:ID: 9a870c05-6d20-0803-d35d-c03fbe1d0ee1 -:END: - -* Version 30.0 [3/3] -** DONE [#A] Fix readcomicsonline browsing to update pages :vrobbler:books:feature:comicbook:personal:project:scrobbling: -:PROPERTIES: -:ID: 981b215a-6473-5fc7-d4cc-51b3eddec4c3 -:END: -** DONE [#B] Redirect webpages back to the original page when starting or stopping :vrobbler:project:webpages:bug: -:PROPERTIES: -:ID: 6183d03a-452b-51d5-cceb-5bfeada947aa -:END: - -** DONE [#B] Fix ComicVine as source for comic book metadata :vrobbler:books:feature:comicbook:personal:project:scrobbling: -:PROPERTIES: -:ID: d22cec3f-117f-f203-33a5-efbefa8a5cee -:END: - -* Version 29.0 [1/1] -** DONE HOTFIX podcast lookups, final - -* Version 28.0 [1/1] -** DONE HOTFIX podcast lookups - -* Version 27.0 [3/3] -** DONE [#A] Fix bug where podcast scrobbling creates duplicate Podcast :project:vrobbler:scrobbling:podcasts:bug:personal: -:PROPERTIES: -:ID: 7377ef6c-5fa7-9e4e-9080-f9810a76118c -:END: - -Rather than pick up an existing Podcast using the podcast title in the mopidy -file name, Vrobbler creates a new podcast with no enriched data. Not a big deal -for my use as the volume of podcasts I listen to makes manual fixes easy. But -it's annoying. - -** DONE [#A] Allow reading comic books from readcomicsoline.ru :vrobbler:books:feature:comicbook:personal:project:scrobbling: -:PROPERTIES: -:ID: 7c7e9ecc-b675-68c3-764f-ef771ce5d88f -:END: - -- Note taken on [2025-09-25 Thu 10:52] - - Things to consider are whether we scrobble the issue on one page, send it to - archivebox? (yes), and how best to enrich the data - -** DONE [#A] Add RSS feed lookups to podcasts :vrobbler:personal:feature:podcasts: -:PROPERTIES: -:ID: d60645b0-7578-97c1-0278-05bd9de4269c -:END: - -- Note taken on [2025-10-14 Tue 10:08] - - Turns out the Podcast plugin for mopidy does a pretty good job of showing the - latest file without having to scroll the bottom using only Muse to not parse - the podcast title name. BUT, now we're getting urls like this: - - https://nsf.libsyn.com/rss#77e01251-cb20-4609-b577-d48e985d2e7b - - This is great, because there's more context there, but it has to read out of - the RSS feed. We should add a check in the podcast util to sniff out the file - referenced in the # in that url and populate the info from there. This should - actually be much more reliable than the current state of the podcast lookup - which depends on the file to be name properly. - - -* Version 26.0 [3/3] -** DONE Clean up templates for scrobble details :vrobbler:personal:bug:templates: -:PROPERTIES: -:ID: 43dc1e02-c110-5b49-0ac7-4c4f7656d1aa -:END: -** DONE Add named locations visited to dashboard :vrobbler:personal:feature:locations:templates: -:PROPERTIES: -:ID: ebc365a1-cef4-f75d-569f-c24b072ef5a4 -:END: -** DONE Add moods to dashboard :vrobbler:moods:feature:templates:personal: -:PROPERTIES: -:ID: c03a38ce-b337-f4fa-adba-aee08d4329f5 -:END: - -* Version 25.0 [3/3] -** DONE Add basic food templates and fix urls :food:vrobbler:personal:project:bug:urls: -:PROPERTIES: -:ID: 3de3459e-8e7e-abba-e068-b919a819d3e3 -:END: -** DONE [#C] Fix how elapsed time is calculated :vrobbler:personal:project:scrobbles:bug: -:PROPERTIES: -:ID: cff58fc4-06ac-8016-4eae-130b51e3c9b7 -:END: -** DONE Fix templates for videos and dashboard links :personal:feature:project:vrobbler:templates: -:PROPERTIES: -:ID: 7debfbaf-cdd8-f49b-57ff-804bfe7c9236 -:END: - -* Version 24.0 [2/2] -** DONE Clean up logdata for various media :personal:feature:project:vrobbler:logdata: -:PROPERTIES: -:ID: d5cce807-1f45-ef19-45a4-9f7069fa2a93 -:END: -** DONE Removed sidebar and add links to headers :personal:feature:templates:scrobbles: -:PROPERTIES: -:ID: 1a1c0aa6-0313-c8be-1676-5d6adddef0a4 -:END: - - -* Version 23.0 [3/3] -** DONE Add dynamic forms for LogData classes :personal:feature:vrobbler:project:forms:logdata: -:PROPERTIES: -:ID: 0db889a1-f262-fba2-7fed-ed99eded1c88 -:END: -** DONE Look in comments for a timestamp for start from BG stats if the time is missing :vrobbler:feature:boardgames:project:personal: -** DONE Fix long play scrobbles to provide better data :vrobbler:feature:scrobbles:longplay:personal:project: -:PROPERTIES: -:ID: 99f6bd77-dc8f-6ed1-0321-32a52c944264 -:END: - -* Version 19.0 [1/1] -** DONE Add periodic check for mood :vrobbler:feature:moods:personal:project: -:PROPERTIES: -:ID: 55404488-c69f-0dd5-838e-1d1e15c873eb -:END: - -* Version 18.7 [1/1] -** DONE Use the timezone history log to fix old Scrobbles that fall into those timezone blocks :vrobbler:chore:scrobbles:project:personal: -:PROPERTIES: -:ID: 9d055ac1-584b-20c8-7ad9-9ce36b329dc7 -:END: - -* Version 18.4 [2/2] -** DONE Track timezone changes for profiles :vrobbler:feature:profiles:personal:project: -:PROPERTIES: -:ID: 89ec867f-29fd-82f1-be17-b49dddc30c78 -:END: -[2025-07-11 14:23] -** DONE Only create a LastFM import if there are files to import :vrobbler:feature:lastfm:importers:project:personal: -:PROPERTIES: -:ID: 7a1456af-c5f1-6385-b34f-0be24a6b65b0 -:END: -- Note taken on [2025-07-20 Sun 16:21] - - This thing is kicking my butt. As it stands it works, but the scrobbles are not assigned to the tracks properly. - -* Version 18.3 [1/1] -** DONE Add timezone awarness to IMAP importer :personal:project:vrobbler:feature:importer:imap:timezones: -:PROPERTIES: -:ID: 05837b7c-96aa-6190-3678-e2ae7c7cac75 -:END: - -* Version 18 [4/4] -** DONE Condense tracks of the same title by the same artist with multiple albums :vrobbler:feature:music:project:personal: -:PROPERTIES: -:ID: b39fcec8-59fd-eab0-5809-b8144c7d2708 -:END: -** DONE Import from BG stats a "learning" log field when "Learning to play" is in the comment :vrobbler:feature:boardgames:project:personal: -:PROPERTIES: -:ID: fda59fab-4349-e99e-54c6-9f1392a1c474 -:END: -** DONE [#A] Add email importer for BG stats file uploads :vrobbler:feature:boardgames:personal:project: -:PROPERTIES: -:ID: 116fe738-7966-615c-d195-ccff0337b101 -:END: -#+begin_src json example of a file -{ - "about": "This is a Play file that can be read by Board Game Stats. If you see this text, try to use a share, export or open-in function to open it with Board Game Stats.", - "players": [ - { - "uuid": "31f8b92e-11d8-4162-88b1-fd9c79eea249", - "id": 2, - "name": "Colin", - "isAnonymous": false, - "modificationDate": "2025-07-01 18:10:32", - "metaData": "{\"isNpc\":0}" - }, - { - "uuid": "00074700-cf4e-4ad3-b334-d35805bb0d90", - "id": 4, - "name": "Asa Sewell", - "isAnonymous": false, - "modificationDate": "2025-07-01 18:03:37" - } - ], - "locations": [ - { - "uuid": "14f7389c-767f-4725-9b35-906c407b293c", - "id": 3, - "name": "Timberwyck Farm", - "modificationDate": "2025-07-01 18:03:38" - } - ], - "games": [ - { - "uuid": "043a2851-f201-467a-a60c-0b0a7e9c33d2", - "id": 333, - "name": "Ghost Fightin' Treasure Hunters: Anniversary Edition", - "modificationDate": "2025-07-02 01:37:14", - "cooperative": true, - "highestWins": true, - "noPoints": false, - "usesTeams": false, - "urlThumb": "https://cf.geekdo-images.com/DHA-mcH3zzw_OjfDxOPj1A__thumb/img/UhaIm4KIDIiraUc44QIvSAbMUXI=/fit-in/200x150/filters:strip_icc()/pic8266874.jpg", - "urlImage": "https://cf.geekdo-images.com/DHA-mcH3zzw_OjfDxOPj1A__original/img/2-Lb6nLePhn0I0Hh2j1pOtbO4rg=/0x0/filters:format(jpeg)/pic8266874.jpg", - "bggName": "Ghost Fightin' Treasure Hunters: Anniversary Edition", - "bggYear": 2024, - "bggId": 422668, - "designers": "Brian Yu", - "isBaseGame": 1, - "isExpansion": 0, - "rating": 75, - "minPlayerCount": 2, - "maxPlayerCount": 5, - "minPlayTime": 30, - "maxPlayTime": 0, - "minAge": 8 - } - ], - "plays": [ - { - "uuid": "bae3f29e-5e1e-45d8-b409-47a665c8d5b5", - "modificationDate": "2025-07-02 01:37:59", - "entryDate": "2025-07-02 01:31:38", - "playDate": "2025-07-02 01:31:38", - "usesTeams": false, - "durationMin": 23, - "ignored": false, - "manualWinner": true, - "rounds": 3, - "scoresheet": "{\"bggId\":244711,\"version\":1,\"langCode\":\"en\",\"scoreType\":\"bestTotalWins\",\"groups\":[{\"templateId\":\"1\",\"maxRepeat\":-1,\"repetition\":1,\"hasSubTotal\":false,\"hideSingleGroupLabel\":false,\"isExtra\":false,\"rows\":[{\"templateId\":\"vptrack\",\"label\":\"VP track\",\"repetition\":1,\"repeatable\":false,\"negative\":false,\"isExtra\":false,\"scores\":{}},{\"templateId\":\"objectives\",\"label\":\"Objectives\",\"repetition\":1,\"repeatable\":false,\"negative\":false,\"isExtra\":false,\"scores\":{}},{\"templateId\":\"mastercards\",\"label\":\"Master cards\",\"repetition\":1,\"repeatable\":false,\"negative\":false,\"isExtra\":false,\"scores\":{}}]}]}", - "locationRefId": 3, - "gameRefId": 333, - "board": "", - "scoringSetting": 4, - "metaData": "{\"playUsedGameCopy\":2}", - "playerScores": [ - { - "score": "", - "winner": true, - "newPlayer": true, - "startPlayer": false, - "playerRefId": 4, - "role": "", - "rank": 0, - "seatOrder": 0, - "metaData": "{\"scoreUuid\":\"00074700-cf4e-4ad3-b334-d35805bb0d90\"}" - }, - { - "score": "", - "winner": true, - "newPlayer": true, - "startPlayer": false, - "playerRefId": 2, - "role": "", - "rank": 0, - "seatOrder": 0, - "metaData": "{\"scoreUuid\":\"31f8b92e-11d8-4162-88b1-fd9c79eea249\"}" - } - ], - "expansionPlays": [] - } - ], - "userInfo": { - "meRefId": 2 - } -} - -#+end_src -** DONE [#B] Fix task app to only use one tag for the context a task was done in and allow configurable contexts by user profile :personal:vrobbler:feature:tasks:project: -:PROPERTIES: -:ID: 23f485e3-988c-6198-c79d-91fdf92f001c -:END: - -* Version 17.0 [6/6] -** DONE [#A] Fix bug in new task label lookup for Emacs/Org-mode :vrobbler:bug:tasks: -:PROPERTIES: -:ID: 683fb109-dfc4-85e4-80f0-ea618434f61e -:END: -** DONE [#C] Replace commas in the bandcamp URL for artists with nothing :vrobbler:music:bug:personal: -:PROPERTIES: -:ID: 9b30d67b-91f0-a480-dfaa-5d9dc090e76c -:END: - -- Note taken on [2025-06-16 Mon 09:36] - - This firt appeared with Black Country, New Road, where the RYM slug generator - leaves commas in and ends up sending you to a 404. I suspect this wont be the - first tweak we'll need to this, as the RYM link creator is really just - guessing based on the artist name at the path. - -** DONE [#A] Investigate new source of video metadata :personal:project:video:imdb: -:PROPERTIES: -:ID: df2b486c-1170-5199-c312-9bc87760d962 -:END: - -Cinemagoer broke and I probably should find a more reilable source of video data. - -- Note taken on [2025-06-13 Fri 11:19] - - TMDB is much more reliable, but does require an API key. That's all setup now, - so hopefully this breaking IMDB crap is over. - -** DONE [#A] IMDB video lookups are failing :personal:bug:video:imdb: -:PROPERTIES: -:ID: 38f1081f-37b4-f4f2-79aa-c1e87eca4b69 -:END: -<2025-06-13 Fri> - -- Note taken on [2025-06-13 Fri 08:24] - - Looks like Cinemagoer is broken: https://github.com/cinemagoer/cinemagoer/issues/537 - -** DONE [#A] Emacs is not syncing notes :personal:scrobbling:emacs:bug: -:PROPERTIES: -:ID: c79cd491-b30f-0945-d84b-b8cac7562791 -:END: -<2025-06-12 Thu 9:30> - -Not sure if the problem is in my Emacs hook sending or Vrobbler itself. - -- Note taken on [2025-06-12 Thu 09:47] - - Adding a quick note to check on it - -- Note taken on [2025-06-12 Thu 09:50] - - Ah ha. All the messing about with the source field meant that I was looking - for `emacs` as a source but the hook was initially setting sources to - `orgmode` I think I prefer `orgmode` as the source, so updating it thusly. - - Fixed in `490d60cbbb1f8bf90b5fc47d8685b15bdc1d485b` - -** DONE [#A] Show the description of a task in the string rep for a scrobble of a Task :personal:project:scrobbling:vrobbler:feature: -:PROPERTIES: -:ID: df58f8d0-fa4a-2037-c7d7-e5388c239042 -:END: - -* Version 0.16.0 [19/19] -** DONE [#A] Jellyfin, bandcamp tracks from Mopidy create duplicate music tracks :bug:scrobbling:music: -:PROPERTIES: -:ID: 670e8634-49b5-dce9-1684-14f2ffb797f1 -:END: -Effectively, any track that comes in without a MusicBrainz ID does some funky -lookup where it doesn't find a track without an MB id and the track title / -artist combination and creates a new track every time. This has to be cleaned up -by condensing the duplicated tracks into the original proper track. - -But it opens a bigger question about how much MB id should the drive the app -lookup. If it can't be depended on to exist from all sources, it really can't be -canonical. Instead, the combination of track title / artist is really the best -we can do. Last.fm also has this problem, where it doesn't know about albums and -definitely does not know or care about MB ids. - -** DONE Add a user profile page with ability to change settings :profiles:improvement: -- Note taken on [2025-04-04 Fri 10:51] - [[orgit-rev:~/src/code.unbl.ink/secstate/vrobbler/::93c16d80ecff4cd1663cf9ec40fbe6d8f58c3e44][~/src/code.unbl.ink/secstate/vrobbler/ (magit-rev 93c16d8)]] - - https://code.unbl.ink/secstate/vrobbler/commit/93c16d80ecff4cd1663cf9ec40fbe6d8f58c3e44 - -** DONE What to do with Youtube videos from LastFM and web-scrobbler :bug:source:lastfm: -- Note taken on [2025-04-04 Fri 10:46] - - Nothing. Over the last few months I built out a youtube model in videos and - use a bookmarklet scrobbling pattern. Now web-scrobbler is just disabled for - Youtube. - - May want to revisit this at some point and only scrobble tracks from Youtube, - because many people use YT for music listening. - -** DONE [#C] Consider a purge command for duplicated and stuck in-progress scrobbles :utililty:improvement: -CLOSED: [2023-04-06 Thu 14:09] -** DONE Add a "stop_timestamp" so we don't rely on content length :improvement:scrobbling: -CLOSED: [2023-04-02 Sun 23:58] - -Essentially, we currently have the timestamp as when the content began -scrobbling and then calculate the finish time from the length of the content. -This works pretty well because we know how long most things are. - -But in some cases, sports events or long podcasts, we may start mid-way through -an event or finish halfway through but still want to mark it as done. In these -cases, knowing the finish time could be useful, especially when interfacing with -other scrobblers which may have different definitions of when a scrobble -finishes or started. -** DONE Fix bug with Various Artist albums being labeled with first artist as album artist :scrobbling:bug:music: -CLOSED: [2023-03-27 Mon 20:18] -:LOGBOOK: -CLOCK: [2023-03-26 Sun 22:01]--[2023-03-27 Mon 01:07] => 3:06 -:END: -** DONE Fix bug with weekly aggregator being blank on Sundays :aggregators:music:bug: -CLOSED: [2023-03-26 Sun 13:52] -** DONE Fix KoReader scrobbling to use pages rather than time of last read :scrobbling:books:improvement: -CLOSED: [2023-03-26 Sun 13:51] -:LOGBOOK: -CLOCK: [2023-03-26 Sun 13:11]--[2023-03-26 Sun 13:51] => 0:40 -:END: -** DONE [#A] Add django-storage to store files on S3 :settings:improvement: -CLOSED: [2023-03-24 Fri 14:46] -:LOGBOOK: -CLOCK: [2023-03-24 Fri 10:47]--[2023-03-24 Fri 14:46] => 3:59 -CLOCK: [2023-03-24 Fri 10:36]--[2023-03-24 Fri 10:40] => 0:04 -:END: -** DONE Fix vrobbler settings not using booleans :settings:bug: -CLOSED: [2023-03-24 Fri 10:45] -:LOGBOOK: -CLOCK: [2023-03-24 Fri 10:40]--[2023-03-24 Fri 10:46] => 0:06 -:END: -** DONE Update weekly live chart to be 7-day continuous rather than weekly :views:bug: -CLOSED: [2023-03-24 Fri 00:31] -The live view will be blank every Monday, no reason to tie it to a day of the -week. It should be "the last 7 days" -** DONE [#B] Implement a detail view for TV shows :improvement:views: -CLOSED: [2023-03-22 Wed 17:05] -** DONE [#B] Implement a detail view for Movies :improvement:views: -CLOSED: [2023-03-22 Wed 17:05] -** DONE Add "service provider" to TV Series, and use that for source when available :bug:scrobbling: -CLOSED: [2023-03-22 Wed 17:04] -** DONE Add view for long-play content (books, video games) to restart them :views:improvement: -CLOSED: [2023-03-22 Wed 17:01] -** DONE Add live chart view like Maloja :improvement:views: -CLOSED: [2023-03-07 Tue 11:13] -** DONE [#C] Figure out how to add to web-scrobbler :improvement:scrobbling: -CLOSED: [2023-03-22 Wed 17:06] - -An example: -https://github.com/web-scrobbler/web-scrobbler/blob/master/src/core/background/scrobbler/maloja-scrobbler.js - -This is actually going to be moot because we can import from LastFM, and -web-scrobbler integrates well with LastFM. The only thing to think through here -now is what to do with all the garbage web-scrobbler sometimes pushes to LastFM -from Youtube (all videos get pushed, sigh). - -** DONE Add Amazon scraper to look up books when OL fails :books:improvement: -This turned out to be a non-starter ... Amazon is aggressive at disallowing -scraping quality. And all the OSS tools out there are stuck in an arms race -trying to keep them from breaking. - -That said, Google Books actually has a decent API (for now), and I've built this -out using that. - -** DONE Fix bug in Jellyfin scrobbles that spam more scrobbles after completion :scrobbling:videos:bug: -This was fixed a while ago, but there's a new manifested bug. Going to create a -separate bug tracking ticket for that. - -* Version 0.11.4 [9/9] -** DONE Add rudimentary video game scrobbling :improvement:content:videogames: -CLOSED: [2023-03-07 Tue 11:11] -** DONE Add ability to scrobble from KOReader statistics files :improvement:books:content: -CLOSED: [2023-03-07 Tue 11:11] - -** DONE [#A] Fix fetching artwork without release group :bug: -CLOSED: [2023-01-29 Sun 14:27] - -When we get artwork from Musicbrianz, and it's not found, we should check for -release groups as well. This will stop issues with missing artwork because of -obscure MB release matches. - -** DONE [#A] Fix Jellyfin music scrobbling N+1 past 90 completion percent :bug: -CLOSED: [2023-01-30 Mon 18:31] -:LOGBOOK: -CLOCK: [2023-01-30 Mon 18:00]--[2023-01-30 Mon 18:31] => 0:31 -:END: - -If we play music from Jellyfin and the track reaches 90% completion, the -scrobbling goes crazy and starts creating new scrobbles with every update. - -The cause is pretty simple, but the solution is hard. We want to mark a scrobble -as complete for the following conditions: - -- Play stopped and percent played beyond 90% -- Play completely finished - -But if we keep listening beyond 90, we should basically ignore updates (or just -update the existing scrobble) -** DONE [#A] Add support for Audioscrobbler tab-separated file uploads :improvement: -CLOSED: [2023-02-03 Fri 16:52] - -An example of the format: -#+begin_src csv -, -#AUDIOSCROBBLER/1.1 -#TZ/UNKNOWN -#CLIENT/Rockbox sansaclipplus $Revision$ -75 Dollar Bill I Was Real I Was Real 4 1015 S 1740494944 64ff5f53-d187-4512-827e-7606c69e66ff -75 Dollar Bill I Was Real I Was Real 4 1015 S 1740494990 64ff5f53-d187-4512-827e-7606c69e66ff -311 311 Down 1 173 S 1740495003 00476c23-fd9e-464b-9b27-a62d69f3d4f4 -311 311 Down 1 173 L 1740495049 00476c23-fd9e-464b-9b27-a62d69f3d4f4 -311 311 Down 1 173 L 1740495113 00476c23-fd9e-464b-9b27-a62d69f3d4f4 -311 311 Random 2 187 S 1740495190 530c09f3-46fe-4d90-b11f-7b63bcb4b373 -311 311 Random 2 187 L 1740495194 530c09f3-46fe-4d90-b11f-7b63bcb4b373 -311 311 Jackolantern’s Weather 3 204 L 1740495382 cc3b2dec-5d99-47ea-8930-20bf258be4ea -311 311 All Mixed Up 4 182 L 1740495586 980a78b5-5bdd-4f50-9e3a-e13261e2817b -311 311 Hive 5 179 L 1740495768 18f6dc98-d3a2-4f81-b967-97359d14c68c -311 311 Guns (Are for Pussies) 6 137 L 1740495948 5e97ed9f-c8cc-4282-9cbe-f8e17aee5128 -311 311 Misdirected Hostility 7 179 S 1740496085 61ff2c1a-fc9c-44c3-8da1-5e50a44245af -, -#+end_src -** DONE [#B] Allow scrobbling music without MB IDs by grabbing them before scrobble :improvement: -CLOSED: [2023-02-17 Fri 00:10] - -This would allow a few nice flows. One, you'd be able to record the play of an -entire album by just dropping the muscibrainz_id in. This could be helpful for -offline listening. It would also mean bad metadata from mopidy would not break -scrobbling. -** DONE When updating musicbrainz IDs, clear and run fetch artwrok :improvement: -CLOSED: [2023-02-17 Fri 00:11] -** DONE [#A] Add ability to manually scrobble albums or tracks from MB :improvement: -CLOSED: [2023-03-07 Tue 11:09] - -Given a UUID from musicbrainz, we should be able to scrobble an album or -individual track. - -** DONE [#C] Implement keeping track of week/month/year chart-toppers :improvement: -CLOSED: [2023-03-07 Tue 11:10] -:LOGBOOK: -CLOCK: [2023-01-30 Mon 16:30]--[2023-01-30 Mon 18:00] => 1:30 -:END: - -Maloja does this cool thing where artists and tracks get recorded as the top -track of a given week, month or year. They get gold, silver or bronze stars for -their place in the time period. - -I could see this being implemented as a separate Chart table which gets -populated at the end of a time period and has a start and end date that defines -a period, along with a one, two, three instance. - -Of course, it could also be a data model without a table, where it runs some fun -calculations, stores it's values in Redis as a long-term lookup table and just -has to re-populate when the server restarts. - -** DONE [#B] Fix ImproperlyConfigured for drink-detail in scrobbles API :bug:api:drinks: -:PROPERTIES: -:ID: 8f2a3b4c-5d6e-7f80-9102-a3b4c5d6e7f8 -:END: - -*** Description - -GET /api/v1/scrobbles/ raised ImproperlyConfigured because -HyperlinkedModelSerializer could not resolve drink-detail — no API -ViewSet existed for the Drink model. - -*** Solution -- Added DrinkSerializer and DrinkViewSet to drinks/api/ -- Registered DrinkViewSet as "drinks" in the DRF router -- Verified /api/v1/scrobbles/ resolves without error diff --git a/scripts/release.py b/scripts/release.py index ad6e92c..64073e5 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -12,11 +12,11 @@ import sys from pathlib import Path PROJECT_FILE = Path("PROJECT.org") +CHANGELOG_FILE = Path("CHANGELOG.org") PYPROJECT_FILE = Path("pyproject.toml") BACKLOG_RE = re.compile(r"^\* Backlog\s+\[(\d+)/(\d+)\](.*)$") VERSION_RE = re.compile(r"^\* Version\s+(\d+\.\d+)\s+\[\d+/\d+\]") -DONE_HEADER_RE = re.compile(r"^(\*\* DONE\s+)(.*)$") ITEM_HEADER_RE = re.compile(r"^\*\* ") @@ -39,6 +39,35 @@ def bump_version(current_major, current_minor, kind): raise ValueError(f"Unknown bump kind: {kind}") +def collect_done_items(backlog_lines): + """Split Backlog into items; return done items and kept items. + + Each item is a list of lines (without the section header). + """ + # Split Backlog into items at each ** line (skip the section header) + items = [] # list of (start_idx, end_idx, is_done) + item_start = None + for i in range(1, len(backlog_lines)): + if ITEM_HEADER_RE.match(backlog_lines[i]): + if item_start is not None: + items.append( + (item_start, i, backlog_lines[item_start].startswith("** DONE")) + ) + item_start = i + if item_start is not None: + items.append( + ( + item_start, + len(backlog_lines), + backlog_lines[item_start].startswith("** DONE"), + ) + ) + + done_items = [backlog_lines[s:e] for s, e, is_done in items if is_done] + kept_items = [backlog_lines[s:e] for s, e, is_done in items if not is_done] + return done_items, kept_items + + def main(): if len(sys.argv) < 2 or sys.argv[1] not in ("major", "minor"): print(f"Usage: {sys.argv[0]} ", file=sys.stderr) @@ -46,56 +75,25 @@ def main(): kind = sys.argv[1] - lines = PROJECT_FILE.read_text().splitlines(keepends=True) + changelog_lines = CHANGELOG_FILE.read_text().splitlines(keepends=True) + project_lines = PROJECT_FILE.read_text().splitlines(keepends=True) # --------------------------------------------------------------- - # 1. Identify top-level sections + # 1. Parse current version from the first * Version header in CHANGELOG.org # --------------------------------------------------------------- - section_starts = [] - for i, line in enumerate(lines): - if line.startswith("* ") and not line.startswith("** "): - section_starts.append(i) - section_starts.append(len(lines)) - - backlog_idx = None - version_idx = None - - for idx, start in enumerate(section_starts[:-1]): - header = lines[start].strip() - if header.startswith("* Backlog"): - backlog_idx = idx - if header.startswith("* Version"): - version_idx = idx # last occurrence wins - - if backlog_idx is None: - print("ERROR: no Backlog section found", file=sys.stderr) - sys.exit(1) - if version_idx is None: - print("ERROR: no Version section found", file=sys.stderr) - sys.exit(1) - - backlog_start = section_starts[backlog_idx] - backlog_end = section_starts[backlog_idx + 1] - - # Find the newest Version section (first after Backlog) that matches - # our expected format (e.g. "37.0" not "0.11.4"). version_start = None - for idx in range(backlog_idx + 1, version_idx + 1): - header = lines[section_starts[idx]].strip() - if VERSION_RE.match(header): - version_start = section_starts[idx] + for i, line in enumerate(changelog_lines): + if VERSION_RE.match(line.strip()): + version_start = i break if version_start is None: - print("ERROR: no parseable Version header found", file=sys.stderr) + print( + "ERROR: no parseable Version header found in CHANGELOG.org", file=sys.stderr + ) sys.exit(1) - version_header = lines[version_start].strip() - - # --------------------------------------------------------------- - # 2. Parse current version from the newest * Version header - # --------------------------------------------------------------- - vm = VERSION_RE.match(version_header) + vm = VERSION_RE.match(changelog_lines[version_start].strip()) current_version = vm.group(1) major_str, minor_str = current_version.split(".") current_major = int(major_str) @@ -104,73 +102,80 @@ def main(): new_version = f"{new_major}.{new_minor}" # --------------------------------------------------------------- - # 3. Collect ** DONE items from the Backlog section + # 2. Identify the Backlog section in PROJECT.org # --------------------------------------------------------------- - backlog_lines = lines[backlog_start:backlog_end] + section_starts = [] + for i, line in enumerate(project_lines): + if line.startswith("* ") and not line.startswith("** "): + section_starts.append(i) + section_starts.append(len(project_lines)) - # Split Backlog into items at each ** line (skip the section header) - items = [] # list of (start_idx, end_idx, is_done) - item_start = None - for i in range(1, len(backlog_lines)): - if ITEM_HEADER_RE.match(backlog_lines[i]): - if item_start is not None: - items.append((item_start, i, backlog_lines[item_start].startswith("** DONE"))) - item_start = i - if item_start is not None: - items.append((item_start, len(backlog_lines), backlog_lines[item_start].startswith("** DONE"))) + backlog_idx = None + for idx, start in enumerate(section_starts[:-1]): + if project_lines[start].strip().startswith("* Backlog"): + backlog_idx = idx - done_items = [(s, e) for s, e, is_done in items if is_done] - kept_items = [(s, e) for s, e, is_done in items if not is_done] + if backlog_idx is None: + print("ERROR: no Backlog section found in PROJECT.org", file=sys.stderr) + sys.exit(1) + + backlog_start = section_starts[backlog_idx] + backlog_end = section_starts[backlog_idx + 1] + backlog_lines = project_lines[backlog_start:backlog_end] + + done_items, kept_items = collect_done_items(backlog_lines) if not done_items: print("No DONE items found in Backlog — nothing to release.") sys.exit(1) # --------------------------------------------------------------- - # 4. Build the new Version section text + # 3. Build the new Version section text # --------------------------------------------------------------- - version_section_lines = [f"* Version {new_version} [{len(done_items)}/{len(done_items)}]\n"] - for s, e in done_items: - version_section_lines.extend(backlog_lines[s:e]) + version_section_lines = [ + f"* Version {new_version} [{len(done_items)}/{len(done_items)}]\n" + ] + for item in done_items: + version_section_lines.extend(item) + version_section_lines.append("\n") # --------------------------------------------------------------- - # 5. Build updated Backlog section + # 4. Build updated Backlog section # --------------------------------------------------------------- backlog_header_line = backlog_lines[0] bm = BACKLOG_RE.match(backlog_header_line.strip()) if not bm: - print(f"ERROR: could not parse backlog header: {backlog_header_line!r}", file=sys.stderr) + print( + f"ERROR: could not parse backlog header: {backlog_header_line!r}", + file=sys.stderr, + ) sys.exit(1) - done_count = int(bm.group(1)) - total_count = int(bm.group(2)) tags = bm.group(3) - new_done = done_count - len(done_items) - new_total = total_count - len(done_items) + new_done = 0 + new_total = len(kept_items) new_backlog_header = f"* Backlog [{new_done}/{new_total}]{tags}\n" backlog_body = [] - for s, e in kept_items: - backlog_body.extend(backlog_lines[s:e]) + for item in kept_items: + backlog_body.extend(item) # --------------------------------------------------------------- - # 6. Assemble the new file + # 5. Assemble the new PROJECT.org (Backlog + DONE items removed) # --------------------------------------------------------------- - before_backlog = lines[:backlog_start] - after_backlog = lines[backlog_end:version_start] + before_backlog = project_lines[:backlog_start] + output_project = before_backlog + [new_backlog_header] + backlog_body + PROJECT_FILE.write_text("".join(output_project)) - # Everything from the first Version section onwards - from_version = lines[version_start:] - - output = ( - before_backlog - + [new_backlog_header] - + backlog_body + # --------------------------------------------------------------- + # 6. Insert the new Version section into CHANGELOG.org after the title + # --------------------------------------------------------------- + output_changelog = ( + changelog_lines[:version_start] + version_section_lines - + ["\n"] - + after_backlog - + from_version + + changelog_lines[version_start:] ) + CHANGELOG_FILE.write_text("".join(output_changelog)) # --------------------------------------------------------------- # 7. Update pyproject.toml @@ -183,19 +188,14 @@ def main(): count=1, flags=re.MULTILINE, ) - - # --------------------------------------------------------------- - # 8. Write files - # --------------------------------------------------------------- - PROJECT_FILE.write_text("".join(output)) PYPROJECT_FILE.write_text(pyproject) # --------------------------------------------------------------- - # 9. Build commit body from done item titles + # 8. Build commit body from done item titles # --------------------------------------------------------------- commit_lines = [] - for s, e in done_items: - title = parse_done_line(backlog_lines[s]) + for item in done_items: + title = parse_done_line(item[0]) if title: commit_lines.append(f"- {title}") @@ -203,14 +203,17 @@ def main(): commit_message = f"[release] Bump to version {new_version}\n\n{commit_body}" # --------------------------------------------------------------- - # 10. Git commit + tag + # 9. Git commit + tag # --------------------------------------------------------------- - subprocess.run(["git", "add", str(PROJECT_FILE), str(PYPROJECT_FILE)], check=True) + subprocess.run( + ["git", "add", str(PROJECT_FILE), str(CHANGELOG_FILE), str(PYPROJECT_FILE)], + check=True, + ) subprocess.run(["git", "commit", "-m", commit_message], check=True) subprocess.run(["git", "tag", new_version], check=True) print(f"\nReleased v{new_version} — tag {new_version} created.") - print(f"Moved {len(done_items)} DONE item(s) from Backlog to Version section.") + print(f"Moved {len(done_items)} DONE item(s) from Backlog to CHANGELOG.org.") if __name__ == "__main__":