├── .dockerignore
├── .eslintrc.js
├── .gitattributes
├── .github
├── CONTRIBUTING.md
├── PULL_REQUEST_TEMPLATE.md
├── release-drafter.yml
└── workflows
│ ├── build-prod-image.yml
│ ├── deploy-image.yml
│ ├── frontend-tests.yml
│ ├── push-dev-image.yml
│ ├── release-drafter.yml
│ ├── spark-tests.yml
│ └── unit-tests.yml
├── .gitignore
├── .pep8speaks.yml
├── .readthedocs.yaml
├── .stylelintrc.js
├── .vscode
└── settings.json
├── .well-known
└── funding-manifest-urls
├── Dockerfile
├── Dockerfile.nginx.prod
├── Dockerfile.spark
├── LICENSE
├── README.md
├── admin
├── config.sh.ctmpl
├── config.sh.sample
├── create-dumps.sh
├── cron_lock.py
├── functions.sh
├── rsync-dump-files.sh
├── sql
│ ├── create_db.sql
│ ├── create_extensions.sql
│ ├── create_foreign_keys.sql
│ ├── create_indexes.sql
│ ├── create_primary_keys.sql
│ ├── create_schema.sql
│ ├── create_tables.sql
│ ├── create_test_db.sql
│ ├── create_types.sql
│ ├── drop_db.sql
│ ├── reset_tables.sql
│ ├── updates
│ │ ├── 2017-06-05-add-last-login.sql
│ │ ├── 2017-06-23-add-latest-import.sql
│ │ ├── 2017-07-03-alter-last-login.sql
│ │ ├── 2017-07-28-add-stats-tables.sql
│ │ ├── 2017-08-03-make-stats-updated-not-null.sql
│ │ ├── 2017-09-03-make-column-names-singular.sql
│ │ ├── 2017-10-14-add-incremental-dump-table.sql
│ │ ├── 2017-10-15-remove-listen-tables.sql
│ │ ├── 2018-05-09-add-on-delete-cascade-to-user-foreign-keys.sql
│ │ ├── 2018-05-22-add-gdpr-user-columns.sql
│ │ ├── 2018-06-13-add-musicbrainz-row-id-column.sql
│ │ ├── 2018-06-21-make-musicbrainz-row-id-not-null.sql
│ │ ├── 2018-06-22-spotify-listen-importer.sql
│ │ ├── 2018-11-17-add-on-delete-cascade-to-spotify-fk.sql
│ │ ├── 2019-01-04-add-user-login-id.sql
│ │ ├── 2019-02-08-save-spotify-permissions.sql
│ │ ├── 2019-02-13-change-login-id-type.sql
│ │ ├── 2019-02-26-add-follow-list.sql
│ │ ├── 2019-07-09-add-recommendation-table.sql
│ │ ├── 2020-05-16-rename-recommendation-table-col.sql
│ │ ├── 2020-05-20-change-lovehate-to-feedback.sql
│ │ ├── 2020-05-20-change-recommendation-cf-recording-col-type.sql
│ │ ├── 2020-05-20-drop-recommendation-cf-recording-col.sql
│ │ ├── 2020-05-20-truncate-user-stats-table.sql
│ │ ├── 2020-06-17-add-listening-activity-col.sql
│ │ ├── 2020-07-13-add-daily-activity-col.sql
│ │ ├── 2020-07-17-add-similar-user-table.sql
│ │ ├── 2020-07-29-add-artist-map-col.sql
│ │ ├── 2020-08-14-add-missing-musicbrainz-data-table.sql
│ │ ├── 2020-08-14-add-sitewide-stats-table.sql
│ │ ├── 2020-09-06-add-user-relationship-table.sql
│ │ ├── 2020-10-03-add-recommendation-feedback-table.sql
│ │ ├── 2021-03-01-remove-follow-list.sql
│ │ ├── 2021-03-02-add-user-recommendation-event-table.sql
│ │ ├── 2021-03-14-add-user-timeline-enum-type-notification.sql
│ │ ├── 2021-04-12-1-new-music-services-table.sql
│ │ ├── 2021-04-12-2-migrate-existing-spotify-users-to-new-schema.sql
│ │ ├── 2021-04-21-1-create-listens-importer-table.sql
│ │ ├── 2021-04-21-2-modify-external-service-oauth-table.sql
│ │ ├── 2021-04-21-3-migrate-existing-spotify-users-second-version.sql
│ │ ├── 2021-05-03-add-email-to-user-table.sql
│ │ ├── 2021-05-04-add-report-users-table.sql
│ │ ├── 2021-05-30-add-pinned-recording-table.sql
│ │ ├── 2021-06-25-add-optional-mbid-to-pinned-recording-table.sql
│ │ ├── 2021-07-10-add-lastfm-external-service-type.sql
│ │ ├── 2021-07-14-migrate-import-ts-from-users-to-listens-importer.sql
│ │ ├── 2021-07-15-add-librefm-external-service-type.sql
│ │ ├── 2021-07-27-add-critiquebrainz-as-external-service.sql
│ │ ├── 2021-08-28-add-user-timeline-enum-type-critiquebrainz_review.sql
│ │ ├── 2021-09-14-new-statistics-table.sql
│ │ ├── 2021-09-28-add-statistics-range-quarter.sql
│ │ ├── 2021-09-28-add-user-name-search-support.sql
│ │ ├── 2021-09-29-add-statistics-range-half-yearly.sql
│ │ ├── 2021-10-07-delete-old-statistics-user-table.sql
│ │ ├── 2021-11-24-add-this-time-ranges.sql
│ │ ├── 2021-12-19-make-msid-optional-for-pinned-recordings.sql
│ │ ├── 2021-12-20-add-mbid-to-recording-feedback.sql
│ │ ├── 2021-16-11-year-in-music.sql
│ │ ├── 2022-03-32-hide-user-timeline-event.sql
│ │ ├── 2022-06-26-add-user-timeline-enum-type-personal-recommendation.sql
│ │ ├── 2022-06-27-user-setting.sql
│ │ ├── 2022-10-12-add-troi-user-setting.sql
│ │ ├── 2022-11-18-add-do-not-recommend-table.sql
│ │ ├── 2022-11-21-remove-unique-index-recording-table.sql
│ │ ├── 2022-12-05-add-year-column-to-yim.sql
│ │ ├── 2023-03-23-add-external-user-id-column-to-external-services-oauth-table.sql
│ │ ├── 2023-04-20-add-musicbrainz-as-external-service.sql
│ │ ├── 2023-04-20-add-soundcloud-as-external-service.sql
│ │ ├── 2023-07-06-add-apple-as-external-service.sql
│ │ ├── 2024-01-15-add-deployments-as-external-service.sql
│ │ ├── 2024-03-18-add-background-tasks.sql
│ │ ├── 2024-04-03-add-brainzplayer-settings.sql
│ │ ├── 2024-07-17-hide-personal-recommendation.sql
│ │ ├── 2024-08-02-make-external-service-access-token-nullable.sql
│ │ ├── 2024-08-06-add-background-export.sql
│ │ ├── 2024-09-09-add-user-flair.sql
│ │ ├── 2025-01-27-add-thanks-event-type.sql
│ │ ├── 2025-02-01-add-user-paused.sql
│ │ ├── 2025-02-21-add-data-dump-type.sql
│ │ └── 2025-05-05-add-import-info.sql
│ └── util
│ │ ├── logout_all_users.sql
│ │ └── restart_spotify_imports.sql
└── timescale
│ ├── create_db.sql
│ ├── create_extensions.sql
│ ├── create_foreign_keys.sql
│ ├── create_indexes.sql
│ ├── create_primary_keys.sql
│ ├── create_schemas.sql
│ ├── create_tables.sql
│ ├── create_test_db.sql
│ ├── create_types.sql
│ ├── create_views.sql
│ ├── drop_db.sql
│ ├── insert_default_data.sql
│ ├── reset_tables.sql
│ └── updates
│ ├── 2020-11-21-playlists.sql
│ ├── 2021-08-03-before-migration-listens-add-user-id.sql
│ ├── 2021-10-27-add-artist-mbids-constraint.sql
│ ├── 2021-10-27-add-listen-mbid-mapping-conditional-not-null-constraint.sql
│ ├── 2021-11-17-refactor-mbid-mapping
│ ├── 2022-01-13-add-listen-helper-table.sql
│ ├── 2022-01-13-add-user-id-indexes.sql
│ ├── 2022-01-27-fixup-created-column.sql
│ ├── 2022-02-02-add-listen-table-delete-column.sql
│ ├── 2022-05-29-add-check-again-column.sql
│ ├── 2022-09-11-migrate-msb.sql
│ ├── 2022-09-21-phase-out-mbid-mapping-metadata.sql
│ ├── 2022-10-10-spotify-cache-normalized-1.sql
│ ├── 2022-10-10-spotify-cache-normalized-2.sql
│ ├── 2022-10-23-add-artist-credit-mbid-similarity.sql
│ ├── 2022-10-23-add-artist-similarity.sql
│ ├── 2022-10-23-add-recording-similarity.sql
│ ├── 2022-11-08-add-additional-metadata-playlists.sql
│ ├── 2022-12-16-add-manual-mbid-mapping.sql
│ ├── 2023-01-04-update-materialized-mapping-view.sql
│ ├── 2023-01-15-new-listens-table.sql
│ ├── 2023-05-05-listens-table-switch.sql
│ ├── 2023-05-06-rename-similarity-tables.sql
│ ├── 2023-05-26-add-popularity-tables.sql
│ ├── 2023-06-130-fix-none-in-recording-mbid.sql
│ ├── 2023-07-17-add-tags-data-tables.sql
│ ├── 2023-09-05-update-column-names-for-spotify-metadata-cache.sql
│ ├── 2023-09-13-add-apple-metadata-cache-tables.sql
│ ├── 2023-11-11-msb-deduplicate-1.sql
│ ├── 2023-11-11-msb-deduplicate-2.sql
│ ├── 2023-12-02-listens-table-switch.sql
│ ├── 2024-05-28-add-pg_trgm-extension.sql
│ ├── 2024-06-26-add-soundcloud-metadata-cache.sql
│ ├── 2024-07-05-add-soundcloud-metadata-cache-2.sql
│ ├── 2025-02-18-add-listen-delete-metadata.sql
│ ├── 2025-02-19-add-user-listen-history-delete.sql
│ └── 2025-02-19-change-listen-delete-metadata-status.sql
├── babel.config.js
├── consul_config.py.ctmpl
├── data
├── __init__.py
├── model
│ ├── __init__.py
│ ├── common_stat.py
│ ├── common_stat_spark.py
│ ├── entity_listener_stat.py
│ ├── external_service.py
│ ├── listen.py
│ ├── new_releases_stat.py
│ ├── sitewide_entity.py
│ ├── user_artist_map.py
│ ├── user_artist_stat.py
│ ├── user_cf_recommendations_recording_message.py
│ ├── user_daily_activity.py
│ ├── user_entity.py
│ ├── user_listening_activity.py
│ ├── user_missing_musicbrainz_data.py
│ ├── user_recording_stat.py
│ ├── user_release_group_stat.py
│ ├── user_release_stat.py
│ └── validators.py
└── postgres
│ ├── __init__.py
│ ├── artist.py
│ ├── artist_credit.py
│ ├── feedback.py
│ ├── recording.py
│ ├── release.py
│ ├── release_group.py
│ └── tag.py
├── develop.sh
├── docker
├── Dockerfile.spark.base
├── apache-download.sh
├── consul-template.conf
├── couchdb_test.ini
├── docker-compose.labs.api.yml
├── docker-compose.spark.override.yml
├── docker-compose.spark.yml
├── docker-compose.test.yml
├── docker-compose.yml
├── lb-startup-common.sh
├── prod
│ └── nginx
│ │ └── nginx.conf
├── push.sh
├── rc.local
├── run-lb-command
├── services
│ ├── api_compat
│ │ ├── api_compat.finish
│ │ ├── api_compat.service
│ │ ├── consul-template-api-compat.conf
│ │ └── uwsgi-api-compat.ini
│ ├── apple_metadata_cache
│ │ ├── apple_metadata_cache.finish
│ │ ├── apple_metadata_cache.service
│ │ └── consul-template-apple-metadata-cache.conf
│ ├── background_tasks
│ │ ├── background_tasks.finish
│ │ ├── background_tasks.service
│ │ └── consul-template-background-tasks.conf
│ ├── cron
│ │ ├── consul-template-cron-config.conf
│ │ ├── cron-config.service
│ │ └── crontab
│ ├── labs_api
│ │ ├── consul-template-labs-api.conf
│ │ ├── labs_api.finish
│ │ ├── labs_api.service
│ │ └── uwsgi-labs-api.ini
│ ├── lastfm_importer
│ │ ├── consul-template-lastfm-importer.conf
│ │ ├── lastfm_importer.finish
│ │ └── lastfm_importer.service
│ ├── mbid_mapping_writer
│ │ ├── consul-template-mbid-mapping-writer.conf
│ │ ├── mbid_mapping_writer.finish
│ │ └── mbid_mapping_writer.service
│ ├── soundcloud_metadata_cache
│ │ ├── consul-template-soundcloud-metadata-cache.conf
│ │ ├── soundcloud_metadata_cache.finish
│ │ └── soundcloud_metadata_cache.service
│ ├── spark_reader
│ │ ├── consul-template-spark-reader.conf
│ │ ├── spark_reader.finish
│ │ └── spark_reader.service
│ ├── spotify_metadata_cache
│ │ ├── consul-template-spotify-metadata-cache.conf
│ │ ├── spotify_metadata_cache.finish
│ │ └── spotify_metadata_cache.service
│ ├── spotify_reader
│ │ ├── consul-template-spotify-reader.conf
│ │ ├── spotify_reader.finish
│ │ └── spotify_reader.service
│ ├── timescale_writer
│ │ ├── consul-template-timescale-writer.conf
│ │ ├── timescale_writer.finish
│ │ └── timescale_writer.service
│ ├── uwsgi
│ │ ├── consul-template-uwsgi.conf
│ │ ├── uwsgi.finish
│ │ ├── uwsgi.ini.ctmpl
│ │ └── uwsgi.service
│ └── websockets
│ │ ├── consul-template-websockets.conf
│ │ ├── websockets.finish
│ │ └── websockets.service
├── spark-cluster-config
│ └── test
│ │ ├── core-site.xml
│ │ ├── hdfs-site.xml
│ │ └── spark-env.sh
├── spark-dev-push.sh
├── start-labs-api.sh
└── start-spark-request-consumer.sh
├── docs
├── .gitignore
├── Makefile
├── conf.py
├── developers
│ ├── architecture.rst
│ ├── commands.rst
│ ├── devel-env.rst
│ ├── develop-sh.rst
│ ├── mapping.rst
│ ├── spark-architecture.rst
│ ├── spark-devel-env.rst
│ └── troubleshooting.rst
├── general
│ └── data-update-intervals.rst
├── images
│ ├── auth-popup.png
│ ├── dataflows-graph.png
│ ├── listen-flow.dot
│ ├── listen-flow.svg
│ ├── release-result.png
│ ├── release-workflow.png
│ ├── request_consumer.png
│ └── user-profile.png
├── index.rst
├── maintainers
│ ├── deploy.rst
│ ├── docker-image.rst
│ ├── dumps.rst
│ ├── mapping.rst
│ ├── pull-requests.rst
│ ├── rabbitmq.rst
│ ├── spotify-reader.rst
│ └── updating-prod-db-schema.rst
├── requirements.txt
└── users
│ ├── api-compat.rst
│ ├── api-usage.rst
│ ├── api
│ ├── art.rst
│ ├── core.rst
│ ├── index.rst
│ ├── metadata.rst
│ ├── misc.rst
│ ├── playlist.rst
│ ├── popularity.rst
│ ├── recommendation.rst
│ ├── recordings.rst
│ ├── settings.rst
│ ├── social.rst
│ └── statistics.rst
│ ├── api_usage_examples
│ ├── get_latest_import.py
│ ├── get_listens.py
│ ├── lookup_metadata.py
│ ├── set_latest_import.py
│ ├── submit_feedback.py
│ └── submit_listens.py
│ ├── clients.rst
│ ├── feedback-json.rst
│ ├── json.rst
│ └── listenbrainz-dumps.rst
├── enzyme.config.ts
├── frontend
├── .gitignore
├── css
│ ├── .gitignore
│ ├── DatePicker.less
│ ├── DateTimePicker.less
│ ├── TimePicker.less
│ ├── accordion.less
│ ├── add-listen-modal.less
│ ├── brainzplayer.less
│ ├── cbreviewmodal.less
│ ├── colors.less
│ ├── donations.less
│ ├── donors-page.less
│ ├── entity-pages.less
│ ├── explore.less
│ ├── flairs.less
│ ├── follow.less
│ ├── fresh-releases.less
│ ├── homepage.less
│ ├── huesound.less
│ ├── import-playlist-modal.less
│ ├── listens-page.less
│ ├── main.less
│ ├── messybrainz.less
│ ├── metadata-viewer.less
│ ├── music-neighborhood.less
│ ├── music-services.less
│ ├── musicbrainz-entity-icons.less
│ ├── new-navbar.less
│ ├── personal-recommendation-modal.less
│ ├── pill.less
│ ├── pinned-recordings.less
│ ├── playlists.less
│ ├── preferences.less
│ ├── rc-slider.less
│ ├── recommendation-page.less
│ ├── release-card.less
│ ├── sass
│ │ └── bootstrap.scss
│ ├── scaffolding.less
│ ├── scroll-container.less
│ ├── search-track.less
│ ├── search.less
│ ├── sidebar.less
│ ├── static
│ │ └── widgets.css
│ ├── stats-art-creator.less
│ ├── stats.less
│ ├── switch.less
│ ├── tags.less
│ ├── theme
│ │ ├── bootstrap
│ │ │ ├── mixins.less
│ │ │ ├── mixins
│ │ │ │ ├── alerts.less
│ │ │ │ ├── background-variant.less
│ │ │ │ ├── border-radius.less
│ │ │ │ ├── buttons.less
│ │ │ │ ├── center-block.less
│ │ │ │ ├── clearfix.less
│ │ │ │ ├── forms.less
│ │ │ │ ├── gradients.less
│ │ │ │ ├── grid-framework.less
│ │ │ │ ├── grid.less
│ │ │ │ ├── hide-text.less
│ │ │ │ ├── image.less
│ │ │ │ ├── labels.less
│ │ │ │ ├── list-group.less
│ │ │ │ ├── nav-divider.less
│ │ │ │ ├── nav-vertical-align.less
│ │ │ │ ├── opacity.less
│ │ │ │ ├── pagination.less
│ │ │ │ ├── panels.less
│ │ │ │ ├── progress-bar.less
│ │ │ │ ├── reset-filter.less
│ │ │ │ ├── reset-text.less
│ │ │ │ ├── resize.less
│ │ │ │ ├── responsive-visibility.less
│ │ │ │ ├── size.less
│ │ │ │ ├── tab-focus.less
│ │ │ │ ├── table-row.less
│ │ │ │ ├── text-emphasis.less
│ │ │ │ ├── text-overflow.less
│ │ │ │ └── vendor-prefixes.less
│ │ │ ├── utilities.less
│ │ │ └── variables.less
│ │ ├── buttons.less
│ │ ├── google-fonts.less
│ │ ├── links.less
│ │ ├── navbars.less
│ │ ├── theme.less
│ │ └── variables.less
│ ├── timeline.less
│ ├── utilities.less
│ └── year-in-music.less
├── fonts
│ └── Inter.woff2
├── img
│ ├── ListenBrainz_logo_no_text.png
│ ├── art
│ │ ├── cover-art-on-floor.png
│ │ ├── yim-2022-shareable-bg.png
│ │ ├── yim-2022-shareable-flames.png
│ │ ├── yim-2022-shareable-magnify.png
│ │ └── yim-2022-shareable-stereo.png
│ ├── broken-cd.jpg
│ ├── cover-art-placeholder.jpg
│ ├── critiquebrainz-logo.svg
│ ├── explore
│ │ ├── cover-art-collage.jpg
│ │ ├── fresh-releases.jpg
│ │ ├── huesound.jpg
│ │ ├── lb-radio-beta.jpg
│ │ ├── link-listens.jpg
│ │ ├── made-with-postgres.png
│ │ ├── music-neighborhood.jpg
│ │ ├── similar-users.jpg
│ │ ├── stats-art-beta.jpg
│ │ ├── stats-art
│ │ │ ├── template-designer-top-10.png
│ │ │ ├── template-designer-top-5.png
│ │ │ ├── template-grid-stats-2.png
│ │ │ ├── template-grid-stats.png
│ │ │ └── template-lps-on-the-floor.png
│ │ ├── year-in-music-2021.jpg
│ │ ├── year-in-music-2022.jpg
│ │ ├── year-in-music-2023.jpg
│ │ └── year-in-music-2024.png
│ ├── favicon-16.png
│ ├── favicon-256.png
│ ├── favicon-32.png
│ ├── homepage
│ │ ├── LB-Data-Provider-Info.png
│ │ ├── LB-Data-Provider.png
│ │ ├── LB-Ethical-Info.png
│ │ ├── LB-Ethical.png
│ │ ├── LB-Headphone.png
│ │ ├── LB-Open-Source-Info.png
│ │ ├── LB-Open-Source.png
│ │ └── LB-Speaker.png
│ ├── icons
│ │ └── angle_double_right_icon.svg
│ ├── listenbrainz-logo.png
│ ├── listenbrainz-logo.svg
│ ├── listenbrainz_logo_icon.svg
│ ├── logo_big.svg
│ ├── mb-entity-icons
│ │ ├── artist.svg
│ │ ├── group.svg
│ │ ├── recording.svg
│ │ └── release.svg
│ ├── meb-icons
│ │ ├── AcousticBrainz.svg
│ │ ├── BookBrainz.svg
│ │ ├── CoverArtArchive.svg
│ │ ├── CritiqueBrainz.svg
│ │ ├── MetaBrainz.svg
│ │ ├── MusicBrainz.svg
│ │ └── Picard.svg
│ ├── messybrainz.svg
│ ├── musicbrainz-16.svg
│ ├── navbar_logo.svg
│ ├── playlist-cover-art
│ │ ├── cover-art_1-0.svg
│ │ ├── cover-art_2-0.svg
│ │ ├── cover-art_3-0.svg
│ │ ├── cover-art_3-1.svg
│ │ ├── cover-art_3-2.svg
│ │ ├── cover-art_4-0.svg
│ │ ├── cover-art_4-1.svg
│ │ ├── cover-art_4-2.svg
│ │ ├── cover-art_4-3.svg
│ │ ├── cover-art_5-0.svg
│ │ └── cover-art_5-1.svg
│ ├── recommendations
│ │ ├── blue-1.svg
│ │ ├── blue-2.svg
│ │ ├── blue-3.svg
│ │ ├── blue-4.svg
│ │ ├── green-1.svg
│ │ ├── green-2.svg
│ │ ├── green-3.svg
│ │ ├── green-4.svg
│ │ ├── no-freshness.png
│ │ ├── red-1.svg
│ │ ├── red-2.svg
│ │ ├── red-3.svg
│ │ └── red-4.svg
│ ├── selfie.jpg
│ ├── share-header.png
│ ├── year-in-music-2021.png
│ ├── year-in-music-2021.svg
│ ├── year-in-music-22
│ │ ├── buddy.png
│ │ ├── magnify.png
│ │ ├── map.png
│ │ ├── stereo.png
│ │ ├── yim-22-logo-small-compressed.png
│ │ └── yim22-logo.png
│ ├── year-in-music-23
│ │ ├── buddies-square.png
│ │ ├── cat.png
│ │ ├── dog-tall.png
│ │ ├── dog.png
│ │ ├── fish.png
│ │ ├── flower.png
│ │ ├── ghost-square.png
│ │ ├── heart-square.png
│ │ ├── heart.png
│ │ ├── peep-square.png
│ │ ├── peep.png
│ │ ├── pick-color.png
│ │ ├── shareable-image-arrows.png
│ │ ├── shareable-image-hug.png
│ │ ├── trunk.png
│ │ ├── worm.png
│ │ ├── yim-23-header.png
│ │ ├── yim-23-logo-small-compressed.png
│ │ └── yim23-logo.png
│ └── year-in-music-24
│ │ ├── autumn
│ │ ├── buddies
│ │ │ ├── yim24-buddy-01.png
│ │ │ ├── yim24-buddy-02.png
│ │ │ ├── yim24-buddy-03.png
│ │ │ ├── yim24-buddy-04.png
│ │ │ ├── yim24-buddy-05.png
│ │ │ ├── yim24-buddy-06.png
│ │ │ ├── yim24-buddy-07.png
│ │ │ ├── yim24-buddy-08.png
│ │ │ ├── yim24-buddy-09.png
│ │ │ └── yim24-buddy-10.png
│ │ ├── playlists
│ │ │ ├── yim24-playlist-01-no-bg.png
│ │ │ ├── yim24-playlist-01.png
│ │ │ ├── yim24-playlist-02-no-bg.png
│ │ │ ├── yim24-playlist-02.png
│ │ │ ├── yim24-playlist-03.png
│ │ │ ├── yim24-playlist-04.png
│ │ │ ├── yim24-playlist-05.png
│ │ │ └── yim24-playlist-06.png
│ │ ├── yim24-01.png
│ │ ├── yim24-02.png
│ │ ├── yim24-03.png
│ │ └── yim24-04.png
│ │ ├── icon-autumn.svg
│ │ ├── icon-spring.svg
│ │ ├── icon-summer.svg
│ │ ├── icon-winter.svg
│ │ ├── spring
│ │ ├── buddies
│ │ │ ├── yim24-buddy-01.png
│ │ │ ├── yim24-buddy-02.png
│ │ │ ├── yim24-buddy-03.png
│ │ │ ├── yim24-buddy-04.png
│ │ │ ├── yim24-buddy-05.png
│ │ │ ├── yim24-buddy-06.png
│ │ │ ├── yim24-buddy-07.png
│ │ │ ├── yim24-buddy-08.png
│ │ │ └── yim24-buddy-09.png
│ │ ├── playlists
│ │ │ ├── yim24-playlist-01-no-bg.png
│ │ │ ├── yim24-playlist-01.png
│ │ │ ├── yim24-playlist-02-no-bg.png
│ │ │ ├── yim24-playlist-02.png
│ │ │ ├── yim24-playlist-03.png
│ │ │ ├── yim24-playlist-04.png
│ │ │ ├── yim24-playlist-05.png
│ │ │ └── yim24-playlist-06.png
│ │ ├── yim24-01.png
│ │ ├── yim24-02.png
│ │ ├── yim24-03.png
│ │ └── yim24-04.png
│ │ ├── summer
│ │ ├── buddies
│ │ │ ├── yim24-buddy-01.png
│ │ │ ├── yim24-buddy-02.png
│ │ │ ├── yim24-buddy-03.png
│ │ │ ├── yim24-buddy-04.png
│ │ │ ├── yim24-buddy-05.png
│ │ │ ├── yim24-buddy-06.png
│ │ │ └── yim24-buddy-07.png
│ │ ├── playlists
│ │ │ ├── yim24-playlist-01-no-bg.png
│ │ │ ├── yim24-playlist-01.png
│ │ │ ├── yim24-playlist-02-no-bg.png
│ │ │ ├── yim24-playlist-02.png
│ │ │ ├── yim24-playlist-03.png
│ │ │ ├── yim24-playlist-04.png
│ │ │ ├── yim24-playlist-05.png
│ │ │ └── yim24-playlist-06.png
│ │ ├── yim24-01.png
│ │ ├── yim24-02.png
│ │ ├── yim24-03.png
│ │ └── yim24-04.png
│ │ ├── winter
│ │ ├── buddies
│ │ │ ├── yim24-buddy-01.png
│ │ │ ├── yim24-buddy-02.png
│ │ │ ├── yim24-buddy-03.png
│ │ │ ├── yim24-buddy-04.png
│ │ │ └── yim24-buddy-05.png
│ │ ├── playlists
│ │ │ ├── yim24-playlist-01-no-bg.png
│ │ │ ├── yim24-playlist-01.png
│ │ │ ├── yim24-playlist-02-no-bg.png
│ │ │ ├── yim24-playlist-02.png
│ │ │ ├── yim24-playlist-03.png
│ │ │ ├── yim24-playlist-04.png
│ │ │ ├── yim24-playlist-05.png
│ │ │ └── yim24-playlist-06.png
│ │ ├── yim24-01.png
│ │ ├── yim24-02.png
│ │ ├── yim24-03.png
│ │ └── yim24-04.png
│ │ ├── yim24-header-all-email.png
│ │ └── yim24-header.png
├── js
│ ├── lib
│ │ ├── bootstrap.bundle.min.js
│ │ ├── bootstrap.bundle.min.js.map
│ │ ├── dragscroll.js
│ │ ├── htmx.min.js
│ │ └── soundcloud-player-api.js
│ ├── src
│ │ ├── about
│ │ │ ├── About.tsx
│ │ │ ├── add-data
│ │ │ │ └── AddData.tsx
│ │ │ ├── current-status
│ │ │ │ └── CurrentStatus.tsx
│ │ │ ├── data
│ │ │ │ └── Data.tsx
│ │ │ ├── donations
│ │ │ │ └── Donate.tsx
│ │ │ ├── layout.tsx
│ │ │ ├── routes
│ │ │ │ └── index.tsx
│ │ │ └── terms-of-service
│ │ │ │ └── TermsOfService.tsx
│ │ ├── album
│ │ │ ├── AlbumPage.tsx
│ │ │ └── utils.tsx
│ │ ├── api
│ │ │ └── auth
│ │ │ │ └── AuthPage.tsx
│ │ ├── artist
│ │ │ └── ArtistPage.tsx
│ │ ├── cb-review
│ │ │ ├── CBReview.tsx
│ │ │ ├── CBReviewForm.tsx
│ │ │ └── CBReviewModal.tsx
│ │ ├── common
│ │ │ ├── Accordion.tsx
│ │ │ ├── Pagination.tsx
│ │ │ ├── UserSearch.tsx
│ │ │ ├── Username.tsx
│ │ │ ├── brainzplayer
│ │ │ │ ├── AppleMusicPlayer.tsx
│ │ │ │ ├── BrainzPlayer.tsx
│ │ │ │ ├── BrainzPlayerContext.tsx
│ │ │ │ ├── BrainzPlayerUI.tsx
│ │ │ │ ├── MenuOptions.tsx
│ │ │ │ ├── MusicPlayer.tsx
│ │ │ │ ├── ProgressBar.tsx
│ │ │ │ ├── Queue.tsx
│ │ │ │ ├── QueueItemCard.tsx
│ │ │ │ ├── SoundcloudPlayer.tsx
│ │ │ │ ├── SpotifyPlayer.tsx
│ │ │ │ ├── VolumeControlButton.tsx
│ │ │ │ ├── YoutubePlayer.tsx
│ │ │ │ └── utils.ts
│ │ │ ├── flairs
│ │ │ │ ├── FlairsExplanationButton.tsx
│ │ │ │ └── FlairsExplanationModal.tsx
│ │ │ ├── listens
│ │ │ │ ├── AddToPlaylist.tsx
│ │ │ │ ├── CoverArtWithFallback.tsx
│ │ │ │ ├── ListenCard.tsx
│ │ │ │ ├── ListenControl.tsx
│ │ │ │ ├── ListenCountCard.tsx
│ │ │ │ ├── ListenFeedbackComponent.tsx
│ │ │ │ ├── ListenPayloadModal.tsx
│ │ │ │ ├── MBIDMappingModal.tsx
│ │ │ │ └── RecommendationFeedbackComponent.tsx
│ │ │ └── stats
│ │ │ │ └── StatsExplanationsModal.tsx
│ │ ├── components
│ │ │ ├── Card.tsx
│ │ │ ├── ConfirmationModal.tsx
│ │ │ ├── Footer.tsx
│ │ │ ├── HorizontalScrollContainer.tsx
│ │ │ ├── Loader.tsx
│ │ │ ├── Navbar.tsx
│ │ │ ├── OpenInMusicBrainz.tsx
│ │ │ ├── Pill.tsx
│ │ │ ├── Sidebar.tsx
│ │ │ ├── Switch.tsx
│ │ │ └── SyndicationFeedModal.tsx
│ │ ├── donors
│ │ │ └── Donors.tsx
│ │ ├── error
│ │ │ └── ErrorBoundary.tsx
│ │ ├── explore
│ │ │ ├── Explore.tsx
│ │ │ ├── ai-brainz
│ │ │ │ └── AIBrainz.tsx
│ │ │ ├── art-creator
│ │ │ │ ├── ArtCreator.tsx
│ │ │ │ ├── components
│ │ │ │ │ ├── ColorPicker.tsx
│ │ │ │ │ ├── Gallery.tsx
│ │ │ │ │ ├── GalleryTile.tsx
│ │ │ │ │ ├── IconTray.tsx
│ │ │ │ │ ├── Preview.tsx
│ │ │ │ │ └── ToggleOption.tsx
│ │ │ │ └── utils.tsx
│ │ │ ├── cover-art-collage
│ │ │ │ ├── 2022
│ │ │ │ │ ├── CoverArtComposite.tsx
│ │ │ │ │ └── data
│ │ │ │ │ │ └── rainbow1-100-7.json
│ │ │ │ ├── 2023
│ │ │ │ │ ├── CoverArtComposite.tsx
│ │ │ │ │ └── data
│ │ │ │ │ │ └── mosaic-2023.json
│ │ │ │ └── SEO.tsx
│ │ │ ├── fresh-releases
│ │ │ │ ├── FreshReleases.tsx
│ │ │ │ ├── components
│ │ │ │ │ ├── ReleaseCard.tsx
│ │ │ │ │ ├── ReleaseCardsGrid.tsx
│ │ │ │ │ ├── ReleaseFilters.tsx
│ │ │ │ │ └── ReleaseTimeline.tsx
│ │ │ │ └── utils.tsx
│ │ │ ├── huesound
│ │ │ │ ├── HueSound.tsx
│ │ │ │ ├── components
│ │ │ │ │ └── ColorWheel.tsx
│ │ │ │ └── utils
│ │ │ │ │ ├── defaultColors.ts
│ │ │ │ │ └── utils.ts
│ │ │ ├── layout.tsx
│ │ │ ├── lb-radio
│ │ │ │ ├── LBRadio.tsx
│ │ │ │ └── components
│ │ │ │ │ ├── Playlist.tsx
│ │ │ │ │ └── Prompt.tsx
│ │ │ ├── music-neighborhood
│ │ │ │ ├── MusicNeighborhood.tsx
│ │ │ │ ├── components
│ │ │ │ │ ├── Panel.tsx
│ │ │ │ │ ├── SearchBox.tsx
│ │ │ │ │ ├── SimilarArtist.tsx
│ │ │ │ │ └── SimilarArtistsGraph.tsx
│ │ │ │ ├── types.d.ts
│ │ │ │ └── utils
│ │ │ │ │ ├── generateTransformedArtists.ts
│ │ │ │ │ └── utils.tsx
│ │ │ ├── routes
│ │ │ │ └── index.tsx
│ │ │ └── similar-users
│ │ │ │ └── SimilarUsers.tsx
│ │ ├── gdpr
│ │ │ └── GDPR.tsx
│ │ ├── home
│ │ │ ├── Blob.tsx
│ │ │ ├── Homepage.tsx
│ │ │ └── NumberCounter.tsx
│ │ ├── hooks
│ │ │ ├── __mocks__
│ │ │ │ └── useFeedbackMap.tsx
│ │ │ └── useFeedbackMap.tsx
│ │ ├── import-data
│ │ │ └── ImportData.tsx
│ │ ├── index.tsx
│ │ ├── lastfm-proxy
│ │ │ └── LastfmProxy.tsx
│ │ ├── lastfm
│ │ │ ├── LibreFMImporter.tsx
│ │ │ └── LibreFMImporterModal.tsx
│ │ ├── layout
│ │ │ ├── LayoutWithBackButton.tsx
│ │ │ └── index.tsx
│ │ ├── listens-offline
│ │ │ └── ListensOffline.tsx
│ │ ├── login
│ │ │ └── Login.tsx
│ │ ├── messybrainz
│ │ │ └── MessyBrainz.tsx
│ │ ├── metadata-viewer
│ │ │ ├── MetadataViewerPage.tsx
│ │ │ ├── components
│ │ │ │ └── MetadataViewer.tsx
│ │ │ └── types.d.ts
│ │ ├── musicbrainz-offline
│ │ │ └── MusicBrainzOffline.tsx
│ │ ├── notifications
│ │ │ ├── AlertNotificationsHOC.tsx
│ │ │ └── Notifications.tsx
│ │ ├── personal-recommendations
│ │ │ ├── NamePill.tsx
│ │ │ ├── PersonalRecommendationsModal.tsx
│ │ │ └── SearchDropDown.tsx
│ │ ├── pins
│ │ │ └── PinRecordingModal.tsx
│ │ ├── player
│ │ │ ├── PlayerPage.tsx
│ │ │ └── routes
│ │ │ │ ├── index.tsx
│ │ │ │ └── listening-now-routes.tsx
│ │ ├── playlists
│ │ │ ├── Playlist.tsx
│ │ │ ├── components
│ │ │ │ ├── CreateOrEditPlaylistModal.tsx
│ │ │ │ ├── DeletePlaylistConfirmationModal.tsx
│ │ │ │ ├── DuplicateTrackModal.tsx
│ │ │ │ ├── PlaylistItemCard.tsx
│ │ │ │ └── PlaylistMenu.tsx
│ │ │ └── utils.tsx
│ │ ├── recent
│ │ │ ├── RecentListens.tsx
│ │ │ └── components
│ │ │ │ └── RecentDonors.tsx
│ │ ├── recommended
│ │ │ └── tracks
│ │ │ │ ├── Info.tsx
│ │ │ │ ├── Layout.tsx
│ │ │ │ ├── Recommendations.tsx
│ │ │ │ └── routes
│ │ │ │ └── index.tsx
│ │ ├── release-group
│ │ │ └── ReleaseGroup.tsx
│ │ ├── release
│ │ │ └── Release.tsx
│ │ ├── report-user
│ │ │ ├── ReportUser.tsx
│ │ │ └── ReportUserModal.tsx
│ │ ├── routes
│ │ │ ├── EntityPages.tsx
│ │ │ ├── index.tsx
│ │ │ ├── redirectRoutes.tsx
│ │ │ └── routes.tsx
│ │ ├── search
│ │ │ ├── AlbumSearch.tsx
│ │ │ ├── ArtistSearch.tsx
│ │ │ ├── PlaylistSearch.tsx
│ │ │ ├── Search.tsx
│ │ │ ├── TrackSearch.tsx
│ │ │ ├── UserSearch.tsx
│ │ │ └── types.d.ts
│ │ ├── settings
│ │ │ ├── Settings.tsx
│ │ │ ├── brainzplayer
│ │ │ │ └── BrainzPlayerSettings.tsx
│ │ │ ├── delete-listens
│ │ │ │ └── DeleteListens.tsx
│ │ │ ├── delete
│ │ │ │ └── DeleteAccount.tsx
│ │ │ ├── export
│ │ │ │ ├── ExportButtons.tsx
│ │ │ │ └── ExportData.tsx
│ │ │ ├── flairs
│ │ │ │ └── FlairsSettings.tsx
│ │ │ ├── import
│ │ │ │ └── ImportListens.tsx
│ │ │ ├── layout.tsx
│ │ │ ├── link-listens
│ │ │ │ ├── LinkListens.tsx
│ │ │ │ └── MultiTrackMBIDMappingModal.tsx
│ │ │ ├── music-services
│ │ │ │ └── details
│ │ │ │ │ ├── MusicServices.tsx
│ │ │ │ │ └── components
│ │ │ │ │ ├── ExternalServiceButton.tsx
│ │ │ │ │ └── ImportStatus.tsx
│ │ │ ├── resettoken
│ │ │ │ └── ResetToken.tsx
│ │ │ ├── routes
│ │ │ │ ├── index.tsx
│ │ │ │ └── redirectRoutes.tsx
│ │ │ ├── select_timezone
│ │ │ │ └── SelectTimezone.tsx
│ │ │ └── troi
│ │ │ │ └── SelectTroiPreferences.tsx
│ │ ├── tags
│ │ │ ├── TagComponent.tsx
│ │ │ └── TagsComponent.tsx
│ │ ├── user-feed
│ │ │ ├── NetworkFeed.tsx
│ │ │ ├── ThanksModal.tsx
│ │ │ ├── UserFeed.tsx
│ │ │ ├── UserFeedLayout.tsx
│ │ │ ├── routes
│ │ │ │ └── index.tsx
│ │ │ └── types.ts
│ │ ├── user
│ │ │ ├── Dashboard.tsx
│ │ │ ├── charts
│ │ │ │ ├── UserEntityChart.tsx
│ │ │ │ ├── components
│ │ │ │ │ └── Bar.tsx
│ │ │ │ └── utils.tsx
│ │ │ ├── components
│ │ │ │ ├── AddAlbumListens.tsx
│ │ │ │ ├── AddListenModal.tsx
│ │ │ │ ├── AddSingleListen.tsx
│ │ │ │ ├── PinnedRecordingCard.tsx
│ │ │ │ └── follow
│ │ │ │ │ ├── CompatibilityCard.tsx
│ │ │ │ │ ├── FollowButton.tsx
│ │ │ │ │ ├── FollowerFollowingModal.tsx
│ │ │ │ │ ├── SimilarUsersModal.tsx
│ │ │ │ │ ├── SimilarityScore.tsx
│ │ │ │ │ ├── UserListModalEntry.tsx
│ │ │ │ │ └── UserSocialNetwork.tsx
│ │ │ ├── layout.tsx
│ │ │ ├── playlists
│ │ │ │ ├── Playlists.tsx
│ │ │ │ ├── components
│ │ │ │ │ ├── ImportAppleMusicPlaylistModal.tsx
│ │ │ │ │ ├── ImportJSPFPlaylistModal.tsx
│ │ │ │ │ ├── ImportSoundCloudPlaylistModal.tsx
│ │ │ │ │ ├── ImportSpotifyPlaylistModal.tsx
│ │ │ │ │ ├── PlaylistCard.tsx
│ │ │ │ │ └── PlaylistsList.tsx
│ │ │ │ └── playlistView.d.ts
│ │ │ ├── recommendations
│ │ │ │ ├── RecommendationsPage.tsx
│ │ │ │ └── components
│ │ │ │ │ ├── RecommendationControl.tsx
│ │ │ │ │ └── RecommendationPlaylistSettings.tsx
│ │ │ ├── routes
│ │ │ │ ├── redirectRoutes.tsx
│ │ │ │ └── userRoutes.tsx
│ │ │ ├── stats
│ │ │ │ ├── UserReports.tsx
│ │ │ │ ├── components
│ │ │ │ │ ├── BarDualTone.tsx
│ │ │ │ │ ├── Choropleth.tsx
│ │ │ │ │ ├── HeatMap.tsx
│ │ │ │ │ ├── UserArtistActivity.tsx
│ │ │ │ │ ├── UserArtistMap.tsx
│ │ │ │ │ ├── UserDailyActivity.tsx
│ │ │ │ │ ├── UserListeningActivity.tsx
│ │ │ │ │ └── UserTopEntity.tsx
│ │ │ │ ├── data
│ │ │ │ │ └── world_countries.json
│ │ │ │ └── utils.tsx
│ │ │ ├── taste
│ │ │ │ ├── UserTaste.tsx
│ │ │ │ └── components
│ │ │ │ │ ├── UserFeedback.tsx
│ │ │ │ │ └── UserPins.tsx
│ │ │ └── year-in-music
│ │ │ │ ├── 2021
│ │ │ │ ├── YearInMusic2021.tsx
│ │ │ │ └── components
│ │ │ │ │ └── ComponentToImage.tsx
│ │ │ │ ├── 2022
│ │ │ │ ├── YearInMusic2022.tsx
│ │ │ │ └── components
│ │ │ │ │ └── MagicShareButton.tsx
│ │ │ │ ├── 2023
│ │ │ │ ├── YearInMusic2023.tsx
│ │ │ │ └── components
│ │ │ │ │ └── ImageShareButtons.tsx
│ │ │ │ ├── 2024
│ │ │ │ └── YearInMusic2024.tsx
│ │ │ │ └── SEO.tsx
│ │ └── utils
│ │ │ ├── APIError.ts
│ │ │ ├── APIService.ts
│ │ │ ├── Dropdown.tsx
│ │ │ ├── ErrorBoundary.tsx
│ │ │ ├── FlairLoader.tsx
│ │ │ ├── GlobalAppContext.tsx
│ │ │ ├── Loader.ts
│ │ │ ├── Playlist.ts
│ │ │ ├── ProtectedRoutes.tsx
│ │ │ ├── QueryClient.ts
│ │ │ ├── ReactQueryDevTools.tsx
│ │ │ ├── RecordingFeedbackManager.tsx
│ │ │ ├── Scrobble.ts
│ │ │ ├── SearchAlbumOrMBID.tsx
│ │ │ ├── SearchTrackOrMBID.tsx
│ │ │ ├── appleMusicTypes.d.ts
│ │ │ ├── constants.ts
│ │ │ ├── coverArtCache.ts
│ │ │ ├── donation.d.ts
│ │ │ ├── icons.ts
│ │ │ ├── musickit.d.ts
│ │ │ ├── soundcloudTypes.d.ts
│ │ │ ├── spotifyTypes.d.ts
│ │ │ ├── types.d.ts
│ │ │ └── utils.tsx
│ └── tests
│ │ ├── __mocks__
│ │ ├── encodeScrobbleOutput.json
│ │ ├── feedProps.json
│ │ ├── freshReleasesDisplaySettings.json
│ │ ├── freshReleasesSitewideData.json
│ │ ├── freshReleasesSitewideFilters.json
│ │ ├── freshReleasesUserData.json
│ │ ├── getFeedbackByMsidResponse.json
│ │ ├── getInfo.json
│ │ ├── getInfoNoPlayCount.json
│ │ ├── getMultipleFeedbackResponse.json
│ │ ├── intersection-observer.tsx
│ │ ├── lastFMPrivateUser.json
│ │ ├── listensTimelineProps.json
│ │ ├── localforage.ts
│ │ ├── lookupMBRelease.json
│ │ ├── lookupMBReleaseFromTrack.json
│ │ ├── matchMedia.ts
│ │ ├── missingMBDataProps.json
│ │ ├── page.json
│ │ ├── pinProps.json
│ │ ├── playlistPageProps.json
│ │ ├── react-toastify.js
│ │ ├── recentListensProps.json
│ │ ├── recentListensPropsOneListen.json
│ │ ├── recentListensPropsPlayingNow.json
│ │ ├── recentListensPropsThreeListens.json
│ │ ├── recommendations.json
│ │ ├── soundcloudSearchResponse.json
│ │ ├── spotifySearchEmptyResponse.json
│ │ ├── spotifySearchResponse.json
│ │ ├── timelineProps.json
│ │ ├── userArtistActivity.json
│ │ ├── userArtistMap.json
│ │ ├── userArtistMapProcessDataArtist.json
│ │ ├── userArtistMapProcessDataListen.json
│ │ ├── userArtists.json
│ │ ├── userArtistsProcessData.json
│ │ ├── userDailyActivity.json
│ │ ├── userDailyActivityProcessData.json
│ │ ├── userFeedbackAPIResponse.json
│ │ ├── userFeedbackProps.json
│ │ ├── userListeningActivityAllTime.json
│ │ ├── userListeningActivityMonth.json
│ │ ├── userListeningActivityProcessDataAllTime.json
│ │ ├── userListeningActivityProcessDataMonth.json
│ │ ├── userListeningActivityProcessDataWeek.json
│ │ ├── userListeningActivityProcessDataYear.json
│ │ ├── userListeningActivityWeek.json
│ │ ├── userListeningActivityYear.json
│ │ ├── userPinsProps.json
│ │ ├── userRecordings.json
│ │ ├── userRecordingsProcessData.json
│ │ ├── userReleaseGroups.json
│ │ ├── userReleaseGroupsProcessData.json
│ │ ├── userReleases.json
│ │ ├── userReleasesProcessData.json
│ │ ├── userSocialNetworkProps.json
│ │ └── year-in-music-data.json
│ │ ├── cb-review
│ │ └── CBReviewModal.test.tsx
│ │ ├── common
│ │ ├── brainzplayer
│ │ │ ├── BrainzPlayer.test.tsx
│ │ │ ├── BrainzPlayerUI.test.tsx
│ │ │ ├── MusicPlayer.test.tsx
│ │ │ ├── SoundcloudPlayer.test.tsx
│ │ │ ├── SpotifyPlayer.test.tsx
│ │ │ └── YoutubePlayer.test.tsx
│ │ └── listens
│ │ │ ├── ListenCard.test.tsx
│ │ │ ├── ListenControl.test.tsx
│ │ │ ├── ListenCountCard.test.tsx
│ │ │ ├── ListensControls.test.tsx
│ │ │ └── RecommendationFeedbackComponent.test.tsx
│ │ ├── components
│ │ ├── Card.test.tsx
│ │ ├── Loader.test.tsx
│ │ └── Pill.test.tsx
│ │ ├── explore
│ │ ├── fresh-releases
│ │ │ └── FreshReleases.test.tsx
│ │ └── huesound
│ │ │ └── HueSound.test.tsx
│ │ ├── jest-setup.ts
│ │ ├── lastfm
│ │ ├── LibreFMImporter.test.tsx
│ │ └── LibreFMImporterModal.test.tsx
│ │ ├── personal-recommendations
│ │ ├── NamePill.test.tsx
│ │ ├── PersonalRecommendationsModal.test.tsx
│ │ └── SearchDropDown.test.tsx
│ │ ├── pins
│ │ ├── PinRecordingModal.test.tsx
│ │ ├── PinnedRecordingCard.test.tsx
│ │ └── UserPins.test.tsx
│ │ ├── playlists
│ │ └── Playlist.test.tsx
│ │ ├── recent
│ │ └── RecentListens.test.tsx
│ │ ├── recommended
│ │ ├── RecommendationControl.test.tsx
│ │ └── Recommendations.test.tsx
│ │ ├── test-react-query.tsx
│ │ ├── test-utils.ts
│ │ ├── test-utils
│ │ └── rtl-test-utils.tsx
│ │ ├── user-feed
│ │ ├── NetworkFeed.test.tsx
│ │ └── UserFeed.test.tsx
│ │ ├── user-settings
│ │ └── SelectTimezone.test.tsx
│ │ ├── user
│ │ ├── Dashboard.test.tsx
│ │ ├── follow
│ │ │ ├── FollowButton.test.tsx
│ │ │ ├── FollowerFollowingModal.test.tsx
│ │ │ ├── SimilarUsersModal.test.tsx
│ │ │ └── UserSocialNetwork.test.tsx
│ │ ├── link-listens
│ │ │ └── LinkListens.test.tsx
│ │ ├── stats
│ │ │ ├── BarDualTone.test.tsx
│ │ │ ├── SimilarityScore.test.tsx
│ │ │ ├── UserArtistActivity.test.tsx
│ │ │ ├── UserArtistMap.test.tsx
│ │ │ ├── UserDailyActivity.test.tsx
│ │ │ ├── UserEntityChart.test.tsx
│ │ │ ├── UserListeningActivity.test.tsx
│ │ │ ├── UserReports.test.tsx
│ │ │ └── UserTopEntity.test.tsx
│ │ └── taste
│ │ │ └── UserFeedback.test.tsx
│ │ └── utils
│ │ ├── APIService.test.ts
│ │ ├── ErrorBoundary.test.tsx
│ │ └── utils.test.tsx
├── robots.txt
└── sound
│ └── 5-seconds-of-silence.mp3
├── jest.config.js
├── listenbrainz
├── __init__.py
├── api_compat.py
├── art
│ ├── cover_art_generator.py
│ └── misc
│ │ └── sample_cover_art_grid_post_request.json
├── background
│ ├── __init__.py
│ ├── background_tasks.py
│ ├── delete.py
│ └── export.py
├── config.py.sample
├── db
│ ├── __init__.py
│ ├── artist.py
│ ├── color.py
│ ├── couchdb.py
│ ├── cover_art.py
│ ├── do_not_recommend.py
│ ├── donation.py
│ ├── dump_entry.py
│ ├── exceptions.py
│ ├── external_service_oauth.py
│ ├── feedback.py
│ ├── fresh_releases.py
│ ├── genre.py
│ ├── lastfm_session.py
│ ├── lastfm_token.py
│ ├── lastfm_user.py
│ ├── lb_radio_artist.py
│ ├── licenses
│ │ ├── COPYING-PublicDomain
│ │ └── README.md
│ ├── listens_importer.py
│ ├── mbid_manual_mapping.py
│ ├── metadata.py
│ ├── missing_musicbrainz_data.py
│ ├── model
│ │ ├── __init__.py
│ │ ├── color.py
│ │ ├── feedback.py
│ │ ├── fresh_releases.py
│ │ ├── mbid_manual_mapping.py
│ │ ├── metadata.py
│ │ ├── pinned_recording.py
│ │ ├── playlist.py
│ │ ├── recommendation_feedback.py
│ │ ├── review.py
│ │ └── user_timeline_event.py
│ ├── msid_mbid_mapping.py
│ ├── musicbrainz_entity.py
│ ├── pinned_recording.py
│ ├── playlist.py
│ ├── popularity.py
│ ├── recommendations_cf_recording.py
│ ├── recommendations_cf_recording_feedback.py
│ ├── recording.py
│ ├── release.py
│ ├── similar_users.py
│ ├── similarity.py
│ ├── spotify.py
│ ├── stats.py
│ ├── tags.py
│ ├── testing.py
│ ├── tests
│ │ ├── __init__.py
│ │ ├── test_color.py
│ │ ├── test_couchdb.py
│ │ ├── test_do_not_recommend.py
│ │ ├── test_dump_entry.py
│ │ ├── test_external_service_oauth.py
│ │ ├── test_feedback.py
│ │ ├── test_lastfm_session.py
│ │ ├── test_lastfm_token.py
│ │ ├── test_lastfm_user.py
│ │ ├── test_listens_importer.py
│ │ ├── test_mbid_manual_mapping.py
│ │ ├── test_missing_musicbrainz_data.py
│ │ ├── test_msid_mbid_mapping.py
│ │ ├── test_pinned_recording.py
│ │ ├── test_playlist.py
│ │ ├── test_recommendations_cf_recording.py
│ │ ├── test_recommendations_cf_recording_feedback.py
│ │ ├── test_similar_users.py
│ │ ├── test_spotify.py
│ │ ├── test_stats.py
│ │ ├── test_user.py
│ │ ├── test_user_relationship.py
│ │ ├── test_user_setting.py
│ │ ├── test_user_timeline_event.py
│ │ ├── test_validators.py
│ │ └── utils.py
│ ├── timescale.py
│ ├── user.py
│ ├── user_relationship.py
│ ├── user_setting.py
│ ├── user_timeline_event.py
│ └── year_in_music.py
├── domain
│ ├── __init__.py
│ ├── apple.py
│ ├── brainz_service.py
│ ├── critiquebrainz.py
│ ├── external_service.py
│ ├── importer_service.py
│ ├── lastfm.py
│ ├── musicbrainz.py
│ ├── soundcloud.py
│ ├── spotify.py
│ └── tests
│ │ ├── __init__.py
│ │ ├── test_critiquebrainz.py
│ │ ├── test_external_service.py
│ │ └── test_spotify.py
├── dumps
│ ├── __init__.py
│ ├── check.py
│ ├── cleanup.py
│ ├── exceptions.py
│ ├── exporter.py
│ ├── importer.py
│ ├── manager.py
│ ├── mapping.py
│ ├── models.py
│ ├── sample.py
│ ├── tables.py
│ └── tests
│ │ ├── __init__.py
│ │ ├── test_dump.py
│ │ └── test_dump_manager.py
├── labs_api
│ ├── __init__.py
│ └── labs
│ │ ├── __init__.py
│ │ ├── api
│ │ ├── __init__.py
│ │ ├── apple
│ │ │ ├── __init__.py
│ │ │ ├── apple_mbid_lookup.py
│ │ │ └── apple_metadata_lookup.py
│ │ ├── artist_country_from_artist_mbid.py
│ │ ├── artist_credit_from_artist_mbid.py
│ │ ├── artist_credit_recording_lookup.py
│ │ ├── artist_credit_recording_release_lookup.py
│ │ ├── base_mbid_mapping.py
│ │ ├── bulk_tag_lookup.py
│ │ ├── explain_mbid_mapping.py
│ │ ├── mbid_mapping.py
│ │ ├── mbid_mapping_release.py
│ │ ├── metadata_index
│ │ │ ├── __init__.py
│ │ │ ├── metadata_index_from_mbid_lookup.py
│ │ │ └── metadata_index_from_metadata_lookup.py
│ │ ├── popular_tags.py
│ │ ├── recording_from_recording_mbid.py
│ │ ├── recording_lookup_base.py
│ │ ├── recording_search.py
│ │ ├── similar_artists.py
│ │ ├── similar_recordings.py
│ │ ├── soundcloud
│ │ │ ├── __init__.py
│ │ │ ├── soundcloud_from_mbid_lookup.py
│ │ │ └── soundcloud_from_metadata_lookup.py
│ │ ├── spotify
│ │ │ ├── __init__.py
│ │ │ ├── spotify_mbid_lookup.py
│ │ │ └── spotify_metadata_lookup.py
│ │ ├── tag_similarity.py
│ │ ├── user_listen_sessions.py
│ │ └── utils.py
│ │ ├── main.py
│ │ └── tests
│ │ ├── __init__.py
│ │ ├── test_artist_country_code_from_artist_mbid.py
│ │ ├── test_artist_credit_from_artist_mbid_query.py
│ │ ├── test_artist_credit_recording_lookup.py
│ │ ├── test_explain_mbid_mapping.py
│ │ ├── test_mbid_mapping.py
│ │ ├── test_recording_from_recording_mbid.py
│ │ ├── test_recording_search.py
│ │ └── test_tag_similarity.py
├── listen.py
├── listens_importer
│ ├── __init__.py
│ ├── base.py
│ ├── lastfm.py
│ ├── spotify.py
│ └── tests
│ │ ├── __init__.py
│ │ ├── data
│ │ ├── spotify_play_no_isrc.json
│ │ └── spotify_play_two_artists.json
│ │ └── test_spotify_read_listens.py
├── listenstore
│ ├── __init__.py
│ ├── dump_listenstore.py
│ ├── redis_listenstore.py
│ ├── tests
│ │ ├── __init__.py
│ │ ├── test_dumplistenstore.py
│ │ ├── test_redislistenstore.py
│ │ ├── test_timescale_utils.py
│ │ ├── test_timescalelistenstore.py
│ │ └── util.py
│ ├── timescale_listenstore.py
│ └── timescale_utils.py
├── manage.py
├── mbid_mapping_writer
│ ├── __init__.py
│ ├── job_queue.py
│ ├── matcher.py
│ ├── mbid_mapper.py
│ ├── mbid_mapper_metadata_api.py
│ ├── mbid_mapping_writer.py
│ └── stop_words.py
├── messybrainz
│ ├── README.md
│ ├── __init__.py
│ ├── exceptions.py
│ ├── tests
│ │ ├── __init__.py
│ │ ├── test_init.py
│ │ └── test_update_msids_from_mapping.py
│ └── update_msids_from_mapping.py
├── metadata_cache
│ ├── __init__.py
│ ├── album_handler.py
│ ├── apple
│ │ ├── __init__.py
│ │ ├── client.py
│ │ ├── handler.py
│ │ └── runner.py
│ ├── consumer.py
│ ├── crawler.py
│ ├── handler.py
│ ├── models.py
│ ├── seeder.py
│ ├── soundcloud
│ │ ├── __init__.py
│ │ ├── client.py
│ │ ├── handler.py
│ │ ├── models.py
│ │ └── runner.py
│ ├── spotify
│ │ ├── __init__.py
│ │ ├── handler.py
│ │ └── runner.py
│ ├── store.py
│ └── unique_queue.py
├── misc
│ └── submit_release.py
├── model
│ ├── __init__.py
│ ├── external_service_oauth.py
│ ├── listens_import.py
│ ├── playlist.py
│ ├── playlist_recording.py
│ ├── reported_users.py
│ ├── user.py
│ └── utils.py
├── rtd_config.py
├── server.py
├── spark
│ ├── __init__.py
│ ├── background.py
│ ├── handlers.py
│ ├── request_manage.py
│ ├── request_queries.json
│ ├── spark_dataset.py
│ ├── spark_reader.py
│ └── tests
│ │ ├── __init__.py
│ │ ├── test_handlers.py
│ │ ├── test_query_list.py
│ │ └── test_request_manage.py
├── testdata
│ ├── additional_info.json
│ ├── artist_name_list.json
│ ├── artists_listeners_db_data_for_api_test.json
│ ├── empty_artist_name.json
│ ├── empty_track_name.json
│ ├── invalid_artist_mbid.json
│ ├── invalid_duration.json
│ ├── invalid_listen_missing_track_metadata.json
│ ├── invalid_listen_nan_in_json.json
│ ├── invalid_listen_null_listened_at.json
│ ├── invalid_listen_null_track_metadata.json
│ ├── invalid_mbid_listens.json
│ ├── invalid_recording_mbid.json
│ ├── invalid_release_mbid.json
│ ├── invalid_release_name.json
│ ├── lastfm_loved_tracks_1.json
│ ├── lastfm_loved_tracks_2.json
│ ├── listen_having_unicode_null.json
│ ├── mb_artist_metadata_cache.json
│ ├── mb_artist_metadata_example.json
│ ├── mb_lookup_metadata_example.json
│ ├── mb_metadata_cache_example.json
│ ├── mb_release_group_metadata_cache_example.json
│ ├── mbid_country_mapping_result.json
│ ├── missing_musicbrainz_data.json
│ ├── multi_duration.json
│ ├── mv_artist_metadata_example.json
│ ├── playing_now_more_than_one_listen.json
│ ├── playing_now_ts.json
│ ├── playing_now_with_duration.json
│ ├── playing_now_with_duration_ms.json
│ ├── playing_now_with_ts.json
│ ├── release_groups_listeners_db_data_for_api_test.json
│ ├── same_batch_duplicates.json
│ ├── same_timestamp_diff_track_valid_single.json
│ ├── same_timestamp_diff_track_valid_single_2.json
│ ├── same_timestamp_diff_track_valid_single_3.json
│ ├── similar_artist_db_data_for_api_test.json
│ ├── single_more_than_one_listen.json
│ ├── sitewide_top_artists_db.json
│ ├── sitewide_top_artists_db_data_for_api_test.json
│ ├── sitewide_top_artists_db_data_for_api_test_month.json
│ ├── sitewide_top_artists_db_data_for_api_test_too_many.json
│ ├── sitewide_top_artists_db_data_for_api_test_week.json
│ ├── sitewide_top_artists_db_data_for_api_test_year.json
│ ├── spotify_recently_played_expected.json
│ ├── spotify_recently_played_submitted.json
│ ├── timescale_listenstore_test_listens.json
│ ├── timescale_listenstore_test_listens_2.json
│ ├── timescale_listenstore_test_listens_over_greater_time_range.json
│ ├── timestamp_before_lfm_founding.json
│ ├── timestamp_in_ns.json
│ ├── too_large_listen.json
│ ├── too_long_tag.json
│ ├── too_many_tags.json
│ ├── top_recording_db_data_for_api_test.json
│ ├── user_artist_map_db.json
│ ├── user_artist_map_db_data_for_api_test.json
│ ├── user_artist_map_db_data_for_api_test_month.json
│ ├── user_artist_map_db_data_for_api_test_week.json
│ ├── user_artist_map_db_data_for_api_test_year.json
│ ├── user_daily_activity_api_output.json
│ ├── user_daily_activity_api_output_month.json
│ ├── user_daily_activity_api_output_week.json
│ ├── user_daily_activity_api_output_year.json
│ ├── user_daily_activity_db.json
│ ├── user_daily_activity_db_data_for_api_test.json
│ ├── user_daily_activity_db_data_for_api_test_month.json
│ ├── user_daily_activity_db_data_for_api_test_week.json
│ ├── user_daily_activity_db_data_for_api_test_year.json
│ ├── user_export_test.json
│ ├── user_fresh_releases.json
│ ├── user_listening_activity_db.json
│ ├── user_listening_activity_db_data_for_api_test.json
│ ├── user_listening_activity_db_data_for_api_test_month.json
│ ├── user_listening_activity_db_data_for_api_test_week.json
│ ├── user_listening_activity_db_data_for_api_test_year.json
│ ├── user_top_artists_db.json
│ ├── user_top_artists_db_data_for_api_test.json
│ ├── user_top_artists_db_data_for_api_test_month.json
│ ├── user_top_artists_db_data_for_api_test_too_many.json
│ ├── user_top_artists_db_data_for_api_test_week.json
│ ├── user_top_artists_db_data_for_api_test_year.json
│ ├── user_top_recordings_db.json
│ ├── user_top_recordings_db_data_for_api_test.json
│ ├── user_top_recordings_db_data_for_api_test_month.json
│ ├── user_top_recordings_db_data_for_api_test_too_many.json
│ ├── user_top_recordings_db_data_for_api_test_week.json
│ ├── user_top_recordings_db_data_for_api_test_year.json
│ ├── user_top_release_groups_db.json
│ ├── user_top_release_groups_db_data_for_api_test.json
│ ├── user_top_release_groups_db_data_for_api_test_month.json
│ ├── user_top_release_groups_db_data_for_api_test_too_many.json
│ ├── user_top_release_groups_db_data_for_api_test_week.json
│ ├── user_top_release_groups_db_data_for_api_test_year.json
│ ├── user_top_releases_db.json
│ ├── user_top_releases_db_data_for_api_test.json
│ ├── user_top_releases_db_data_for_api_test_month.json
│ ├── user_top_releases_db_data_for_api_test_too_many.json
│ ├── user_top_releases_db_data_for_api_test_week.json
│ ├── user_top_releases_db_data_for_api_test_year.json
│ ├── valid_duration.json
│ ├── valid_import.json
│ ├── valid_playing_now.json
│ └── valid_single.json
├── tests
│ ├── __init__.py
│ ├── integration
│ │ ├── __init__.py
│ │ ├── test_api.py
│ │ ├── test_api_compat.py
│ │ ├── test_api_compat_deprecated.py
│ │ ├── test_atom_feeds.py
│ │ ├── test_do_not_recommend_api.py
│ │ ├── test_export.py
│ │ ├── test_feed_api.py
│ │ ├── test_feedback_api.py
│ │ ├── test_fresh_release_api.py
│ │ ├── test_missing_musicbrainz_data_api.py
│ │ ├── test_pinned_recording_api.py
│ │ ├── test_playlist_api.py
│ │ ├── test_recommendations_cf_api.py
│ │ ├── test_recommendations_cf_recording_feedback_api.py
│ │ ├── test_settings_views.py
│ │ ├── test_spotify_read_listens.py
│ │ ├── test_stats_api.py
│ │ ├── test_timescale_writer.py
│ │ ├── test_user_settings_api.py
│ │ ├── test_user_timeline_event_api.py
│ │ └── test_websockets.py
│ ├── unit
│ │ ├── test_listen.py
│ │ └── test_utils.py
│ └── utils.py
├── timescale_writer
│ ├── __init__.py
│ └── timescale_writer.py
├── troi
│ ├── __init__.py
│ ├── daily_jams.py
│ ├── export.py
│ ├── import_ms.py
│ ├── spark.py
│ ├── tests
│ │ ├── __init__.py
│ │ └── test_spark.py
│ ├── utils.py
│ ├── weekly_playlists.py
│ └── year_in_music.py
├── utils.py
├── webserver
│ ├── __init__.py
│ ├── admin
│ │ ├── __init__.py
│ │ ├── test_admin.py
│ │ └── views.py
│ ├── converters.py
│ ├── decorators.py
│ ├── errors.py
│ ├── flash.py
│ ├── login
│ │ ├── __init__.py
│ │ ├── copy_files_from_mb_to_lb.py
│ │ └── provider.py
│ ├── models.py
│ ├── rabbitmq_connection.py
│ ├── redis_connection.py
│ ├── static_manager.py
│ ├── templates
│ │ ├── admin
│ │ │ ├── home.html
│ │ │ └── master.html
│ │ ├── art
│ │ │ ├── index.html
│ │ │ └── svg-templates
│ │ │ │ ├── designer-top-10-alt.svg
│ │ │ │ ├── designer-top-10.svg
│ │ │ │ ├── designer-top-5.svg
│ │ │ │ ├── lps-on-the-floor.svg
│ │ │ │ ├── macros.j2
│ │ │ │ ├── simple-grid.svg
│ │ │ │ ├── year-in-music-2024
│ │ │ │ ├── yim-2024-albums.svg
│ │ │ │ ├── yim-2024-artists.svg
│ │ │ │ ├── yim-2024-discovery-playlist.svg
│ │ │ │ ├── yim-2024-missed-tracks-playlist.svg
│ │ │ │ ├── yim-2024-overview.svg
│ │ │ │ ├── yim-2024-stats.svg
│ │ │ │ └── yim-2024-tracks.svg
│ │ │ │ ├── yim-2022-albums.svg
│ │ │ │ ├── yim-2022-artists.svg
│ │ │ │ ├── yim-2022-playlists.svg
│ │ │ │ ├── yim-2022-tracks.svg
│ │ │ │ ├── yim-2022.svg
│ │ │ │ ├── yim-2023-albums.svg
│ │ │ │ ├── yim-2023-artists.svg
│ │ │ │ ├── yim-2023-playlist-arrows.svg
│ │ │ │ ├── yim-2023-playlist-hug.svg
│ │ │ │ ├── yim-2023-stats.svg
│ │ │ │ ├── yim-2023-tracks.svg
│ │ │ │ └── yim-2023.svg
│ │ ├── atom
│ │ │ ├── cb_review_event.html
│ │ │ ├── follow_event.html
│ │ │ ├── fresh_releases.html
│ │ │ ├── listen_event.html
│ │ │ ├── listens.html
│ │ │ ├── notification_event.html
│ │ │ ├── personal_recommendation_event.html
│ │ │ ├── playlist.html
│ │ │ ├── recording.html
│ │ │ ├── recording_pin_event.html
│ │ │ ├── recording_recommendation_event.html
│ │ │ ├── top_albums.html
│ │ │ ├── top_artists.html
│ │ │ └── top_tracks.html
│ │ ├── base.html
│ │ ├── emails
│ │ │ ├── artist_relation_import_notification.txt
│ │ │ ├── cf_candidate_sets_upload_notification.txt
│ │ │ ├── cf_recording_dataframes_upload_notification.txt
│ │ │ ├── cf_recording_model_upload_notification.txt
│ │ │ ├── cf_recording_recommendation_notification.txt
│ │ │ ├── data_dump_created_notification.txt
│ │ │ ├── data_dump_outdated.txt
│ │ │ ├── dump_import_failure.txt
│ │ │ ├── export_completed.txt
│ │ │ ├── id_paused.txt
│ │ │ ├── id_unpaused.txt
│ │ │ ├── listens_importer_error.txt
│ │ │ ├── mapping_import_notification.txt
│ │ │ ├── similar_users_failed_notification.txt
│ │ │ ├── similar_users_updated_notification.txt
│ │ │ ├── user_stats_notification.txt
│ │ │ ├── year_in_music.html
│ │ │ └── year_in_music.txt
│ │ ├── errors
│ │ │ ├── 400.html
│ │ │ ├── 401.html
│ │ │ ├── 403.html
│ │ │ ├── 404.html
│ │ │ ├── 413.html
│ │ │ ├── 500.html
│ │ │ ├── 503.html
│ │ │ └── base.html
│ │ ├── index.html
│ │ ├── macros.html
│ │ ├── navbar.html
│ │ └── widgets
│ │ │ ├── pin.html
│ │ │ ├── pin_card.html
│ │ │ ├── playing_now.html
│ │ │ └── playing_now_card.html
│ ├── test
│ │ ├── __init__.py
│ │ ├── test_api_errors.py
│ │ ├── test_routes.py
│ │ └── test_utils.py
│ ├── testing.py
│ ├── timescale_connection.py
│ ├── utils.py
│ └── views
│ │ ├── __init__.py
│ │ ├── api.py
│ │ ├── api_compat.py
│ │ ├── api_compat_deprecated.py
│ │ ├── api_tools.py
│ │ ├── art.py
│ │ ├── art_api.py
│ │ ├── atom.py
│ │ ├── color_api.py
│ │ ├── do_not_recommend_api.py
│ │ ├── donor_api.py
│ │ ├── donors.py
│ │ ├── entity_pages.py
│ │ ├── explore.py
│ │ ├── explore_api.py
│ │ ├── export.py
│ │ ├── feedback_api.py
│ │ ├── fresh_releases.py
│ │ ├── index.py
│ │ ├── login.py
│ │ ├── metadata_api.py
│ │ ├── metadata_viewer.py
│ │ ├── missing_musicbrainz_data_api.py
│ │ ├── pinned_recording_api.py
│ │ ├── player.py
│ │ ├── playlist.py
│ │ ├── playlist_api.py
│ │ ├── popularity_api.py
│ │ ├── recommendations_cf_recording.py
│ │ ├── recommendations_cf_recording_api.py
│ │ ├── recommendations_cf_recording_feedback_api.py
│ │ ├── settings.py
│ │ ├── social_api.py
│ │ ├── stats_api.py
│ │ ├── status_api.py
│ │ ├── test
│ │ ├── __init__.py
│ │ ├── test_art.py
│ │ ├── test_explore.py
│ │ ├── test_index.py
│ │ ├── test_login.py
│ │ ├── test_player.py
│ │ ├── test_recommendations_cf_recording.py
│ │ ├── test_settings.py
│ │ ├── test_status.py
│ │ └── test_user.py
│ │ ├── user.py
│ │ ├── user_settings_api.py
│ │ ├── user_timeline_event_api.py
│ │ └── views_utils.py
└── websockets
│ ├── __init__.py
│ ├── listens_dispatcher.py
│ └── websockets.py
├── listenbrainz_spark
├── README.md
├── __init__.py
├── config.py.sample
├── constants.py
├── dump
│ ├── __init__.py
│ ├── ftp.py
│ ├── local.py
│ └── tests
│ │ ├── __init__.py
│ │ └── test_ftp.py
├── echo
│ ├── __init__.py
│ └── echo.py
├── exceptions.py
├── fresh_releases
│ ├── __init__.py
│ ├── fresh_releases.py
│ └── tests
│ │ ├── __init__.py
│ │ └── test_fresh_releases.py
├── hdfs
│ ├── __init__.py
│ ├── upload.py
│ └── utils.py
├── hdfs_connection.py
├── listens
│ ├── __init__.py
│ ├── cache.py
│ ├── compact.py
│ ├── data.py
│ ├── delete.py
│ ├── dump.py
│ ├── metadata.py
│ └── tests
│ │ ├── __init__.py
│ │ ├── test_dump.py
│ │ └── test_utils.py
├── missing_mb_data
│ ├── __init__.py
│ ├── missing_mb_data.py
│ └── tests
│ │ ├── __init__.py
│ │ └── test_missing_mb_data.py
├── mlhd
│ ├── __init__.py
│ └── download.py
├── path.py
├── popularity
│ ├── __init__.py
│ ├── common.py
│ ├── listens.py
│ ├── main.py
│ └── mlhd.py
├── postgres
│ ├── __init__.py
│ ├── artist.py
│ ├── artist_credit.py
│ ├── feedback.py
│ ├── recording.py
│ ├── release.py
│ ├── release_group.py
│ ├── tag.py
│ └── utils.py
├── query_map.py
├── recommendations
│ ├── README.md
│ ├── __init__.py
│ ├── dataframe_utils.py
│ ├── recording
│ │ ├── __init__.py
│ │ ├── create_dataframes.py
│ │ ├── discovery.py
│ │ ├── recommend.py
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── test_dataframe.py
│ │ │ ├── test_models.py
│ │ │ └── test_recommend.py
│ │ └── train_models.py
│ ├── templates
│ │ ├── index.html
│ │ └── model.html
│ ├── tests
│ │ ├── __init__.py
│ │ └── test_dataframe_utils.py
│ └── utils.py
├── request_consumer
│ ├── __init__.py
│ ├── request_consumer.py
│ └── test_request_consumer.py
├── schema.py
├── similarity
│ ├── __init__.py
│ ├── artist.py
│ ├── recording
│ │ ├── __init__.py
│ │ ├── common.py
│ │ ├── listens.py
│ │ └── mlhd.py
│ └── user.py
├── stats
│ ├── __init__.py
│ ├── common
│ │ ├── __init__.py
│ │ └── listening_activity.py
│ ├── incremental
│ │ ├── __init__.py
│ │ ├── incremental_stats_engine.py
│ │ ├── listener
│ │ │ ├── __init__.py
│ │ │ ├── artist.py
│ │ │ ├── entity.py
│ │ │ └── release_group.py
│ │ ├── message_creator.py
│ │ ├── query_provider.py
│ │ ├── range_selector.py
│ │ ├── sitewide
│ │ │ ├── __init__.py
│ │ │ ├── artist.py
│ │ │ ├── artist_map.py
│ │ │ ├── entity.py
│ │ │ ├── listening_activity.py
│ │ │ ├── recording.py
│ │ │ ├── release.py
│ │ │ └── release_group.py
│ │ └── user
│ │ │ ├── __init__.py
│ │ │ ├── artist.py
│ │ │ ├── artist_map.py
│ │ │ ├── daily_activity.py
│ │ │ ├── entity.py
│ │ │ ├── listening_activity.py
│ │ │ ├── recording.py
│ │ │ ├── release.py
│ │ │ └── release_group.py
│ ├── listener
│ │ ├── __init__.py
│ │ ├── entity.py
│ │ └── tests
│ │ │ ├── __init__.py
│ │ │ └── test_entity_stats.py
│ ├── sitewide
│ │ ├── __init__.py
│ │ ├── entity.py
│ │ ├── listening_activity.py
│ │ └── tests
│ │ │ ├── __init__.py
│ │ │ └── test_sitewide_artist.py
│ ├── tests
│ │ ├── __init__.py
│ │ └── test_init.py
│ └── user
│ │ ├── __init__.py
│ │ ├── daily_activity.py
│ │ ├── entity.py
│ │ ├── listening_activity.py
│ │ └── tests
│ │ ├── __init__.py
│ │ ├── test_listening_activity_range_selector.py
│ │ └── test_user_stats.py
├── tags
│ ├── __init__.py
│ └── tags.py
├── testdata
│ ├── artist_country_code.parquet
│ ├── artist_credit_mbid.parquet
│ ├── full-dump-1
│ │ ├── 0.parquet
│ │ ├── 1.parquet
│ │ ├── 2.parquet
│ │ ├── 3.parquet
│ │ ├── 4.parquet
│ │ ├── 5.parquet
│ │ ├── 6.parquet
│ │ ├── COPYING
│ │ ├── END_TIMESTAMP
│ │ ├── SCHEMA_SEQUENCE
│ │ └── START_TIMESTAMP
│ ├── import_metadata.json
│ ├── incremental-dump-2
│ │ ├── 0.parquet
│ │ ├── COPYING
│ │ ├── END_TIMESTAMP
│ │ ├── SCHEMA_SEQUENCE
│ │ └── START_TIMESTAMP
│ ├── incremental-dump-3
│ │ ├── 0.parquet
│ │ ├── COPYING
│ │ ├── END_TIMESTAMP
│ │ ├── SCHEMA_SEQUENCE
│ │ └── START_TIMESTAMP
│ ├── incremental-dump-4
│ │ └── fresh_releases_listens.parquet
│ ├── incremental-dump-5
│ │ └── rec_listens.parquet
│ ├── mapped_listens.parquet
│ ├── mapped_listens_candidate_sets.parquet
│ ├── mapped_listens_subset.parquet
│ ├── missing_musicbrainz_data.json
│ ├── recording_artist.parquet
│ ├── release_group_metadata_cache.parquet
│ ├── release_metadata_cache.parquet
│ ├── sitewide_fresh_releases.json
│ ├── sitewide_top_artists_all_time.json
│ ├── user_daily_activity_all_time.json
│ ├── user_fresh_releases_output.json
│ ├── user_listening_activity_all_time.json
│ ├── user_top_artist_listeners_output.json
│ ├── user_top_artists_output.json
│ ├── user_top_recordings_output.json
│ ├── user_top_release_group_listeners_output.json
│ ├── user_top_release_groups_output.json
│ └── user_top_releases_output.json
├── tests
│ ├── __init__.py
│ └── test_utils.py
├── troi
│ ├── __init__.py
│ └── periodic_jams.py
├── utils.py
└── year_in_music
│ ├── __init__.py
│ ├── day_of_week.py
│ ├── listen_count.py
│ ├── listening_time.py
│ ├── listens_per_day.py
│ ├── most_listened_year.py
│ ├── new_artists_discovered.py
│ ├── new_releases_of_top_artists.py
│ ├── similar_users.py
│ ├── top_discoveries.py
│ ├── top_genres.py
│ ├── top_missed_recordings.py
│ ├── top_stats.py
│ └── utils.py
├── manage.py
├── mbid_mapping
├── Dockerfile
├── README.md
├── __init__.py
├── admin
│ └── data_dump_files
│ │ ├── COPYING
│ │ ├── README
│ │ └── README_data_dump_files.md
├── build.sh
├── config.py.sample
├── cron_job.py
├── docker
│ ├── consul-template.conf
│ ├── consul_config.py.ctmpl
│ ├── crontab
│ ├── mapper.service
│ └── push.sh
├── manage.py
├── manage_cron.py
├── mapping
│ ├── __init__.py
│ ├── album_metadata_index.py
│ ├── apple_metadata_index.py
│ ├── bulk_table.py
│ ├── canonical_musicbrainz_data.py
│ ├── canonical_musicbrainz_data_base.py
│ ├── canonical_musicbrainz_data_release_support.py
│ ├── canonical_recording_redirect.py
│ ├── canonical_recording_release_redirect.py
│ ├── canonical_release.py
│ ├── canonical_release_redirect.py
│ ├── cube.py
│ ├── custom_sorts.py
│ ├── mapping_test
│ │ ├── __init__.py
│ │ ├── mapping_test.py
│ │ └── mapping_test_cases.csv
│ ├── mb_artist_metadata_cache.py
│ ├── mb_cache_base.py
│ ├── mb_metadata_cache.py
│ ├── mb_release_group_cache.py
│ ├── release_colors.py
│ ├── search.py
│ ├── soundcloud_metadata_index.py
│ ├── spotify_metadata_index.py
│ ├── typesense_index.py
│ └── utils.py
├── reports
│ ├── top_discoveries.py
│ └── tracks_of_the_year.py
├── requirements.txt
└── similar
│ └── tag_similarity.py
├── mlhd_manage.py
├── package-lock.json
├── package.json
├── pytest.ini
├── pytest.spark.ini
├── requirements.txt
├── requirements_development.txt
├── requirements_spark.txt
├── spark_config.sh.sample
├── spark_manage.py
├── test.sh
├── tsconfig.json
└── webpack.config.js
/.dockerignore:
--------------------------------------------------------------------------------
1 | /.git*
2 |
3 | # Byte-compiled / optimized / DLL files
4 | **/__pycache__/
5 | **/*.py[cod]
6 |
7 | # Virtual environment
8 | /env*/
9 | /venv*/
10 | /build*/
11 |
12 | # Logs
13 | **/*.log
14 | **/pip-log.txt
15 | **/pip-delete-this-directory.txt
16 |
17 | # Test results
18 | **/htmlcov/
19 | **/.coverage
20 |
21 | # Private credentials
22 | /credentials
23 |
24 | # Javascript packages
25 | /node_modules/
26 |
27 | # ListenBrainz local dump directory
28 | /listenbrainz-export
29 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/release-drafter.yml:
--------------------------------------------------------------------------------
1 | template: |
2 | ## What’s Changed
3 |
4 | $CHANGES
--------------------------------------------------------------------------------
/.github/workflows/release-drafter.yml:
--------------------------------------------------------------------------------
1 | name: Release Drafter
2 |
3 | on:
4 | push:
5 | # branches to consider in the event; optional, defaults to all
6 | branches:
7 | - master
8 |
9 | jobs:
10 | update_release_draft:
11 | runs-on: ubuntu-latest
12 | steps:
13 | # Drafts your next Release notes as Pull Requests are merged into "master"
14 | - uses: release-drafter/release-drafter@v6
15 | env:
16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
17 |
--------------------------------------------------------------------------------
/.pep8speaks.yml:
--------------------------------------------------------------------------------
1 | scanner:
2 | diff_only: True # If False, the entire file touched by the Pull Request is scanned for errors. If True, only the diff is scanned.
3 | linter: pycodestyle # Other option is flake8
4 |
5 | pycodestyle: # Same as scanner.linter value. Other option is flake8
6 | max-line-length: 130 # Default is 79 in PEP 8
7 |
8 | no_blank_comment: True # If True, no comment is made on PR without any errors.
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | build:
4 | os: ubuntu-24.04
5 | tools:
6 | python: "3.13"
7 |
8 | sphinx:
9 | configuration: docs/conf.py
10 |
11 | formats: all
12 |
13 | python:
14 | install:
15 | - requirements: docs/requirements.txt
16 |
--------------------------------------------------------------------------------
/.stylelintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: "stylelint-config-recommended-less",
3 | customSyntax: "postcss-less",
4 | plugins: ["stylelint-prettier"],
5 | rules: {
6 | "prettier/prettier": true,
7 | "no-descending-specificity": null,
8 | "function-calc-no-unspaced-operator": null,
9 | },
10 | ignoreFiles: [
11 | "**/static/css/theme/bootstrap/*",
12 | "**/static/css/theme/bootstrap/mixins/*",
13 | ],
14 | }
15 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "[python]": {
3 | "editor.defaultFormatter": "ms-python.autopep8"
4 | },
5 | "python.formatting.provider": "none"
6 | }
--------------------------------------------------------------------------------
/.well-known/funding-manifest-urls:
--------------------------------------------------------------------------------
1 | https://metabrainz.org/funding.json
2 |
--------------------------------------------------------------------------------
/Dockerfile.nginx.prod:
--------------------------------------------------------------------------------
1 | FROM nginx:1.13.5
2 |
3 | RUN apt-get update \
4 | && apt-get install -y --no-install-recommends
5 |
6 | WORKDIR /etc
7 | COPY ./docker/prod/nginx/nginx.conf /etc/nginx/conf.d/default.conf
8 |
--------------------------------------------------------------------------------
/admin/sql/create_db.sql:
--------------------------------------------------------------------------------
1 | -- Create the user and the database. Must run as user postgres.
2 |
3 | CREATE USER listenbrainz NOCREATEDB NOSUPERUSER;
4 | ALTER USER listenbrainz WITH PASSWORD 'listenbrainz';
5 | CREATE DATABASE listenbrainz WITH OWNER = listenbrainz TEMPLATE template0 ENCODING = 'UNICODE';
6 |
--------------------------------------------------------------------------------
/admin/sql/create_extensions.sql:
--------------------------------------------------------------------------------
1 | CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
2 | CREATE EXTENSION IF NOT EXISTS "pg_trgm";
3 | -- The following line is now executed by the init-db action from manage.py. If you create a DB without the init-db function
4 | -- you will need to execute the following ALTER in order to complete your DB setup.
5 | --ALTER DATABASE listenbrainz SET pg_trgm.word_similarity_threshold = 0.1;
6 | CREATE EXTENSION IF NOT EXISTS "cube";
7 |
--------------------------------------------------------------------------------
/admin/sql/create_schema.sql:
--------------------------------------------------------------------------------
1 | CREATE SCHEMA api_compat;
2 | CREATE SCHEMA recommendation;
3 |
--------------------------------------------------------------------------------
/admin/sql/create_test_db.sql:
--------------------------------------------------------------------------------
1 | -- Create the user and the database. Must run as user postgres.
2 |
3 | CREATE USER lb_test NOCREATEDB NOSUPERUSER;
4 | CREATE DATABASE lb_test WITH OWNER = lb_test TEMPLATE template0 ENCODING = 'UNICODE';
5 |
--------------------------------------------------------------------------------
/admin/sql/drop_db.sql:
--------------------------------------------------------------------------------
1 | DROP DATABASE IF EXISTS listenbrainz;
2 | DROP USER IF EXISTS listenbrainz;
3 |
--------------------------------------------------------------------------------
/admin/sql/updates/2017-06-05-add-last-login.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | -- Add column for last login to user table
4 | ALTER TABLE "user" ADD COLUMN last_login TIMESTAMP WITH TIME ZONE;
5 |
6 | COMMIT;
7 |
--------------------------------------------------------------------------------
/admin/sql/updates/2017-06-23-add-latest-import.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | -- Add latest_import timestamp column to "user" table
4 | ALTER TABLE "user" ADD COLUMN latest_import TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT TIMESTAMP 'epoch';
5 |
6 | COMMIT;
7 |
--------------------------------------------------------------------------------
/admin/sql/updates/2017-07-03-alter-last-login.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | -- First update all null values with the created value for that row
4 | UPDATE "user" SET last_login = created WHERE last_login IS NULL;
5 |
6 | -- Alter the last_login column to add the NOT NULL constraint
7 | ALTER TABLE "user" ALTER COLUMN last_login SET NOT NULL;
8 |
9 | -- Alter the last_login column so that the default value becomes NOW()
10 | ALTER TABLE "user" ALTER COLUMN last_login SET DEFAULT NOW();
11 |
12 | COMMIT;
13 |
--------------------------------------------------------------------------------
/admin/sql/updates/2017-10-14-add-incremental-dump-table.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | CREATE TABLE data_dump (
4 | id SERIAL,
5 | created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
6 | );
7 |
8 | COMMIT;
9 |
--------------------------------------------------------------------------------
/admin/sql/updates/2017-10-15-remove-listen-tables.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | DROP TABLE IF EXISTS listen CASCADE;
4 | DROP TABLE IF EXISTS listen_json CASCADE;
5 |
6 | COMMIT;
7 |
--------------------------------------------------------------------------------
/admin/sql/updates/2018-05-22-add-gdpr-user-columns.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE "user" ADD COLUMN gdpr_agreed TIMESTAMP WITH TIME ZONE;
4 |
5 | COMMIT;
6 |
7 |
--------------------------------------------------------------------------------
/admin/sql/updates/2018-06-13-add-musicbrainz-row-id-column.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | -- Add musicbrainz_row_id column to the "user" table
4 | ALTER TABLE "user" ADD COLUMN musicbrainz_row_id INTEGER;
5 | ALTER TABLE "user" ADD CONSTRAINT user_musicbrainz_row_id_key UNIQUE (musicbrainz_row_id);
6 |
7 | COMMIT;
8 |
--------------------------------------------------------------------------------
/admin/sql/updates/2018-06-21-make-musicbrainz-row-id-not-null.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE "user" ALTER COLUMN musicbrainz_row_id SET NOT NULL;
4 |
5 | COMMIT;
6 |
--------------------------------------------------------------------------------
/admin/sql/updates/2018-11-17-add-on-delete-cascade-to-spotify-fk.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE spotify_auth
4 | DROP CONSTRAINT spotify_auth_user_id_foreign_key;
5 |
6 | ALTER TABLE spotify_auth
7 | ADD CONSTRAINT spotify_auth_user_id_foreign_key
8 | FOREIGN KEY (user_id)
9 | REFERENCES "user" (id)
10 | ON DELETE CASCADE;
11 |
12 | COMMIT;
13 |
--------------------------------------------------------------------------------
/admin/sql/updates/2019-01-04-add-user-login-id.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | -- Add column for alternative user id for login purpose to user table
4 | ALTER TABLE "user" ADD COLUMN user_login_id UUID NOT NULL DEFAULT uuid_generate_v4();
5 |
6 | COMMIT;
--------------------------------------------------------------------------------
/admin/sql/updates/2019-02-08-save-spotify-permissions.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | -- rename active to record_listens
4 | ALTER TABLE spotify_auth RENAME active TO record_listens;
5 |
6 | -- add column permission to table
7 | ALTER TABLE spotify_auth ADD COLUMN permission VARCHAR;
8 | -- set value to 'user-read-recently-played' for all current users
9 | UPDATE spotify_auth
10 | SET permission = 'user-read-recently-played';
11 | -- set the column as not null
12 | ALTER TABLE spotify_auth ALTER COLUMN permission SET NOT NULL;
13 |
14 | COMMIT;
15 |
--------------------------------------------------------------------------------
/admin/sql/updates/2019-02-13-change-login-id-type.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE "user" DROP COLUMN user_login_id;
4 | ALTER TABLE "user" ADD COLUMN login_id TEXT NOT NULL DEFAULT uuid_generate_v4()::text;
5 |
6 | UPDATE "user"
7 | SET login_id = id::text;
8 |
9 | ALTER TABLE "user" ADD CONSTRAINT user_login_id_key UNIQUE (login_id);
10 | CREATE UNIQUE INDEX login_id_ndx_user ON "user" (login_id);
11 |
12 | COMMIT;
13 |
14 |
--------------------------------------------------------------------------------
/admin/sql/updates/2020-05-16-rename-recommendation-table-col.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE recommendation.cf_recording
4 | RENAME COLUMN recording_msid TO recording_mbid;
5 |
6 | COMMIT;
7 |
--------------------------------------------------------------------------------
/admin/sql/updates/2020-05-20-change-recommendation-cf-recording-col-type.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE recommendation.cf_recording DROP recording_mbid;
4 | ALTER TABLE recommendation.cf_recording ADD COLUMN recording_mbid JSONB NOT NULL;
5 |
6 | ALTER TABLE recommendation.cf_recording ADD CONSTRAINT user_id_unique UNIQUE (user_id);
7 |
8 | COMMIT;
9 |
--------------------------------------------------------------------------------
/admin/sql/updates/2020-05-20-drop-recommendation-cf-recording-col.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE recommendation.cf_recording DROP TYPE;
4 |
5 | COMMIT;
6 |
--------------------------------------------------------------------------------
/admin/sql/updates/2020-05-20-truncate-user-stats-table.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | TRUNCATE TABLE statistics.user;
4 |
5 | COMMIT;
6 |
--------------------------------------------------------------------------------
/admin/sql/updates/2020-06-17-add-listening-activity-col.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE statistics.user ADD COLUMN listening_activity JSONB;
4 |
5 | COMMIT;
6 |
--------------------------------------------------------------------------------
/admin/sql/updates/2020-07-13-add-daily-activity-col.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE statistics.user ADD COLUMN daily_activity JSONB;
4 |
5 | COMMIT;
6 |
--------------------------------------------------------------------------------
/admin/sql/updates/2020-07-17-add-similar-user-table.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | CREATE TABLE recommendation.similar_user (
4 | user_id INTEGER NOT NULL, -- FK to "user".id
5 | similar_users JSONB,
6 | last_updated TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
7 | );
8 |
9 | ALTER TABLE recommendation.similar_user
10 | ADD CONSTRAINT similar_user_user_id_foreign_key
11 | FOREIGN KEY (user_id)
12 | REFERENCES "user" (id)
13 | ON DELETE CASCADE;
14 |
15 | CREATE UNIQUE INDEX user_id_ndx_similar_user ON recommendation.similar_user (user_id);
16 |
17 | COMMIT;
18 |
--------------------------------------------------------------------------------
/admin/sql/updates/2020-07-29-add-artist-map-col.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE statistics.user ADD COLUMN artist_map JSONB;
4 |
5 | COMMIT;
6 |
--------------------------------------------------------------------------------
/admin/sql/updates/2020-08-14-add-sitewide-stats-table.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | CREATE TABLE statistics.sitewide (
4 | id SERIAL, --pk
5 | stats_range TEXT,
6 | artist JSONB,
7 | release JSONB,
8 | recording JSONB,
9 | listening_activity JSONB,
10 | last_updated TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
11 | );
12 |
13 | ALTER TABLE statistics.sitewide ADD CONSTRAINT stats_range_uniq UNIQUE (stats_range);
14 |
15 | COMMIT;
16 |
--------------------------------------------------------------------------------
/admin/sql/updates/2021-03-01-remove-follow-list.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | DROP TABLE IF EXISTS follow_list;
4 |
5 | COMMIT
--------------------------------------------------------------------------------
/admin/sql/updates/2021-03-14-add-user-timeline-enum-type-notification.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TYPE user_timeline_event_type_enum ADD VALUE 'notification' AFTER 'recording_recommendation';
4 |
5 | COMMIT;
--------------------------------------------------------------------------------
/admin/sql/updates/2021-04-21-2-modify-external-service-oauth-table.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | TRUNCATE TABLE external_service_oauth CASCADE;
4 |
5 | ALTER TABLE external_service_oauth ADD COLUMN scopes TEXT[];
6 | ALTER TABLE external_service_oauth DROP COLUMN service_details;
7 | ALTER TABLE external_service_oauth DROP COLUMN record_listens;
8 |
9 | COMMIT;
--------------------------------------------------------------------------------
/admin/sql/updates/2021-05-03-add-email-to-user-table.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 | ALTER TABLE "user" ADD COLUMN email TEXT;
3 | COMMIT;
--------------------------------------------------------------------------------
/admin/sql/updates/2021-06-25-add-optional-mbid-to-pinned-recording-table.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | -- Changes: recording_mbid column is now optional
4 | -- added a required recording_msid column,
5 |
6 | ALTER TABLE pinned_recording
7 | ALTER COLUMN recording_mbid DROP NOT NULL,
8 | ADD COLUMN recording_msid UUID NOT NULL;
9 |
10 | COMMIT;
--------------------------------------------------------------------------------
/admin/sql/updates/2021-07-10-add-lastfm-external-service-type.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE external_service_oauth_type ADD VALUE 'lastfm';
2 |
--------------------------------------------------------------------------------
/admin/sql/updates/2021-07-14-migrate-import-ts-from-users-to-listens-importer.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 | INSERT INTO listens_importer(user_id, service, latest_listened_at)
3 | SELECT id, 'lastfm', latest_import
4 | FROM "user"
5 | WHERE latest_import > 'epoch';
6 | COMMIT;
7 |
--------------------------------------------------------------------------------
/admin/sql/updates/2021-07-15-add-librefm-external-service-type.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE external_service_oauth_type ADD VALUE 'librefm';
2 |
--------------------------------------------------------------------------------
/admin/sql/updates/2021-07-27-add-critiquebrainz-as-external-service.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE external_service_oauth_type ADD VALUE 'critiquebrainz';
2 |
--------------------------------------------------------------------------------
/admin/sql/updates/2021-08-28-add-user-timeline-enum-type-critiquebrainz_review.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE user_timeline_event_type_enum ADD VALUE 'critiquebrainz_review' AFTER 'notification';
2 |
--------------------------------------------------------------------------------
/admin/sql/updates/2021-09-28-add-statistics-range-quarter.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE stats_range_type ADD VALUE 'quarter' AFTER 'month';
2 |
--------------------------------------------------------------------------------
/admin/sql/updates/2021-09-28-add-user-name-search-support.sql:
--------------------------------------------------------------------------------
1 | CREATE EXTENSION IF NOT EXISTS "pg_trgm";
2 | ALTER DATABASE listenbrainz SET pg_trgm.word_similarity_threshold = 0.1;
3 | BEGIN;
4 | CREATE INDEX CONCURRENTLY user_name_search_trgm_idx ON "user" USING GIST (musicbrainz_id gist_trgm_ops);
5 | COMMIT;
6 |
--------------------------------------------------------------------------------
/admin/sql/updates/2021-09-29-add-statistics-range-half-yearly.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE stats_range_type ADD VALUE 'half_yearly' AFTER 'quarter';
2 |
--------------------------------------------------------------------------------
/admin/sql/updates/2021-10-07-delete-old-statistics-user-table.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 | DROP TABLE IF EXISTS statistics.user;
3 |
4 | ALTER TABLE statistics.user_new RENAME TO "user";
5 | ALTER SEQUENCE statistics.user_new_id_seq RENAME TO user_id_seq;
6 | ALTER TABLE statistics.user RENAME CONSTRAINT stats_user_new_pkey TO stats_user_pkey;
7 | ALTER TABLE statistics.user RENAME CONSTRAINT user_stats_new_user_id_foreign_key TO user_stats_user_id_foreign_key;
8 | ALTER INDEX statistics.user_id_ndx__user_stats_new RENAME TO user_id_ndx__user_stats;
9 | COMMIT;
10 |
--------------------------------------------------------------------------------
/admin/sql/updates/2021-11-24-add-this-time-ranges.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE stats_range_type ADD VALUE 'this_week';
2 | ALTER TYPE stats_range_type ADD VALUE 'this_month';
3 | ALTER TYPE stats_range_type ADD VALUE 'this_year';
4 |
--------------------------------------------------------------------------------
/admin/sql/updates/2021-12-19-make-msid-optional-for-pinned-recordings.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE pinned_recording ALTER COLUMN recording_msid DROP NOT NULL;
4 |
5 | ALTER TABLE pinned_recording
6 | ADD CONSTRAINT pinned_rec_recording_msid_or_recording_mbid_check
7 | CHECK ( recording_msid IS NOT NULL OR recording_mbid IS NOT NULL );
8 |
9 | COMMIT;
10 |
--------------------------------------------------------------------------------
/admin/sql/updates/2021-12-20-add-mbid-to-recording-feedback.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE recording_feedback ALTER COLUMN recording_msid DROP NOT NULL;
4 |
5 | ALTER TABLE recording_feedback ADD COLUMN recording_mbid UUID;
6 |
7 | ALTER TABLE recording_feedback
8 | ADD CONSTRAINT feedback_recording_msid_or_recording_mbid_check
9 | CHECK ( recording_msid IS NOT NULL OR recording_mbid IS NOT NULL );
10 |
11 | CREATE UNIQUE INDEX user_id_mbid_ndx_rec_feedback ON recording_feedback (user_id, recording_mbid);
12 |
13 | COMMIT;
14 |
--------------------------------------------------------------------------------
/admin/sql/updates/2021-16-11-year-in-music.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | CREATE TABLE statistics.year_in_music (
4 | user_id INTEGER NOT NULL, -- PK and FK to "user".id
5 | data JSONB
6 | );
7 |
8 | ALTER TABLE statistics.year_in_music ADD CONSTRAINT stats_year_in_music_pkey PRIMARY KEY (user_id);
9 |
10 | ALTER TABLE statistics.year_in_music
11 | ADD CONSTRAINT user_stats_year_in_music_user_id_foreign_key
12 | FOREIGN KEY (user_id)
13 | REFERENCES "user" (id)
14 | ON DELETE CASCADE;
15 |
16 | COMMIT;
17 |
--------------------------------------------------------------------------------
/admin/sql/updates/2022-06-26-add-user-timeline-enum-type-personal-recommendation.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE user_timeline_event_type_enum ADD VALUE 'personal_recording_recommendation' AFTER 'critiquebrainz_review';
2 |
--------------------------------------------------------------------------------
/admin/sql/updates/2022-10-12-add-troi-user-setting.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE user_setting ADD COLUMN troi JSONB;
4 |
5 | COMMIT;
--------------------------------------------------------------------------------
/admin/sql/updates/2022-11-21-remove-unique-index-recording-table.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | DROP INDEX user_id_mbid_ndx_rec_feedback;
4 | CREATE INDEX user_id_mbid_ndx_rec_feedback ON recording_feedback (user_id, recording_mbid);
5 |
6 | COMMIT;
7 |
--------------------------------------------------------------------------------
/admin/sql/updates/2023-03-23-add-external-user-id-column-to-external-services-oauth-table.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE external_service_oauth ADD COLUMN external_user_id TEXT;
4 |
5 | COMMIT;
--------------------------------------------------------------------------------
/admin/sql/updates/2023-04-20-add-musicbrainz-as-external-service.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE external_service_oauth_type ADD VALUE 'musicbrainz';
2 |
--------------------------------------------------------------------------------
/admin/sql/updates/2023-04-20-add-soundcloud-as-external-service.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE external_service_oauth_type ADD VALUE 'soundcloud';
2 |
--------------------------------------------------------------------------------
/admin/sql/updates/2023-07-06-add-apple-as-external-service.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE external_service_oauth_type ADD VALUE 'apple';
2 |
--------------------------------------------------------------------------------
/admin/sql/updates/2024-01-15-add-deployments-as-external-service.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE external_service_oauth_type ADD VALUE 'musicbrainz-prod';
2 | ALTER TYPE external_service_oauth_type ADD VALUE 'musicbrainz-beta';
3 | ALTER TYPE external_service_oauth_type ADD VALUE 'musicbrainz-test';
4 |
--------------------------------------------------------------------------------
/admin/sql/updates/2024-04-03-add-brainzplayer-settings.sql:
--------------------------------------------------------------------------------
1 |
2 | BEGIN;
3 |
4 | ALTER TABLE user_setting
5 | ADD brainzplayer JSONB;
6 |
7 | COMMIT;
--------------------------------------------------------------------------------
/admin/sql/updates/2024-07-17-hide-personal-recommendation.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE hide_user_timeline_event_type_enum ADD VALUE 'personal_recording_recommendation' BEFORE 'recording_pin';
2 |
--------------------------------------------------------------------------------
/admin/sql/updates/2024-08-02-make-external-service-access-token-nullable.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE external_service_oauth ALTER COLUMN access_token DROP NOT NULL;
4 |
5 | COMMIT;
6 |
--------------------------------------------------------------------------------
/admin/sql/updates/2024-09-09-add-user-flair.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE user_setting ADD COLUMN flair JSONB;
4 |
5 | COMMIT;
6 |
--------------------------------------------------------------------------------
/admin/sql/updates/2025-01-27-add-thanks-event-type.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE user_timeline_event_type_enum ADD VALUE 'thanks' AFTER 'personal_recording_recommendation';
2 |
--------------------------------------------------------------------------------
/admin/sql/updates/2025-02-01-add-user-paused.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE "user" ADD COLUMN is_paused BOOLEAN NOT NULL DEFAULT FALSE;
4 |
5 | COMMIT;
6 |
--------------------------------------------------------------------------------
/admin/sql/updates/2025-02-21-add-data-dump-type.sql:
--------------------------------------------------------------------------------
1 | CREATE TYPE data_dump_type_type AS ENUM ('incremental', 'full');
2 |
3 | BEGIN;
4 |
5 | ALTER TABLE data_dump ADD COLUMN dump_type data_dump_type_type;
6 |
7 | COMMIT;
8 |
--------------------------------------------------------------------------------
/admin/sql/updates/2025-05-05-add-import-info.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE "listens_importer" ADD COLUMN status JSONB;
4 |
5 | COMMIT;
6 |
--------------------------------------------------------------------------------
/admin/sql/util/logout_all_users.sql:
--------------------------------------------------------------------------------
1 | -- we use this script when we need to log out all users.
2 | -- see https://flask-login.readthedocs.io/en/latest/#alternative-tokens for how/why it works
3 | BEGIN;
4 | UPDATE "user"
5 | SET login_id = uuid_generate_v4()::text;
6 | COMMIT;
7 |
--------------------------------------------------------------------------------
/admin/sql/util/restart_spotify_imports.sql:
--------------------------------------------------------------------------------
1 | -- This script resets the record listens flag for all
2 | -- users, restarting their imports.
3 |
4 | BEGIN;
5 |
6 | UPDATE listens_importer
7 | SET error_message = NULL
8 | WHERE service = 'spotify';
9 |
10 | COMMIT;
11 |
--------------------------------------------------------------------------------
/admin/timescale/create_db.sql:
--------------------------------------------------------------------------------
1 | -- Create the user and the database. Must run as user postgres.
2 |
3 | CREATE USER listenbrainz_ts NOCREATEDB NOSUPERUSER;
4 | ALTER USER listenbrainz_ts WITH PASSWORD 'listenbrainz_ts';
5 | CREATE DATABASE listenbrainz_ts WITH OWNER = listenbrainz_ts TEMPLATE template1 ENCODING = 'UNICODE';
6 |
--------------------------------------------------------------------------------
/admin/timescale/create_extensions.sql:
--------------------------------------------------------------------------------
1 | CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;
2 | CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
3 | CREATE EXTENSION IF NOT EXISTS "pg_trgm";
4 |
--------------------------------------------------------------------------------
/admin/timescale/create_primary_keys.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE playlist.playlist ADD CONSTRAINT playlist_pkey PRIMARY KEY (id);
4 | ALTER TABLE playlist.playlist_recording ADD CONSTRAINT playlist_recording_pkey PRIMARY KEY (id);
5 | ALTER TABLE mbid_mapping_metadata ADD CONSTRAINT mbid_mapping_metadata_pkey PRIMARY KEY (recording_mbid);
6 | ALTER TABLE mapping.mb_metadata_cache ADD CONSTRAINT mb_metadata_cache_pkey PRIMARY KEY (recording_mbid);
7 | ALTER TABLE background_worker_state ADD CONSTRAINT background_worker_state_pkey PRIMARY KEY (key);
8 |
9 | COMMIT;
10 |
--------------------------------------------------------------------------------
/admin/timescale/create_schemas.sql:
--------------------------------------------------------------------------------
1 | CREATE SCHEMA playlist;
2 | CREATE SCHEMA messybrainz;
3 | CREATE SCHEMA mapping;
4 | CREATE SCHEMA spotify_cache;
5 | CREATE SCHEMA apple_cache;
6 | CREATE SCHEMA soundcloud_cache;
7 | CREATE SCHEMA similarity;
8 | CREATE SCHEMA tags;
9 | CREATE SCHEMA popularity;
10 | CREATE SCHEMA statistics; -- this schema is used to store the YIM tables, tables are created dynamically in listenbrainz/db/year_in_music.py
11 |
--------------------------------------------------------------------------------
/admin/timescale/create_test_db.sql:
--------------------------------------------------------------------------------
1 | -- Create the user and the database. Must run as user postgres.
2 |
3 | CREATE USER listenbrainz_ts_test NOCREATEDB NOSUPERUSER;
4 | CREATE DATABASE listenbrainz_ts_test WITH OWNER = listenbrainz_ts_test TEMPLATE template0 ENCODING = 'UNICODE';
5 |
--------------------------------------------------------------------------------
/admin/timescale/create_types.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | CREATE TYPE mbid_mapping_match_type_enum AS ENUM('no_match', 'low_quality', 'med_quality', 'high_quality', 'exact_match');
4 | CREATE TYPE lb_tag_radio_source_type_enum AS ENUM ('recording', 'artist', 'release-group');
5 | CREATE TYPE listen_delete_metadata_status_enum AS ENUM ('pending', 'invalid', 'complete');
6 |
7 | COMMIT;
8 |
--------------------------------------------------------------------------------
/admin/timescale/drop_db.sql:
--------------------------------------------------------------------------------
1 | DROP DATABASE IF EXISTS listenbrainz_ts;
2 | DROP USER IF EXISTS listenbrainz_ts;
3 |
--------------------------------------------------------------------------------
/admin/timescale/reset_tables.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | DELETE FROM listen CASCADE;
4 | DELETE FROM listen_delete_metadata CASCADE;
5 | DELETE FROM listen_user_metadata CASCADE;
6 | DELETE FROM mbid_mapping CASCADE;
7 | DELETE FROM mapping.mb_metadata_cache CASCADE;
8 | DELETE FROM messybrainz.submissions CASCADE;
9 | DELETE FROM mbid_manual_mapping CASCADE;
10 | DELETE FROM playlist.playlist CASCADE;
11 |
12 | COMMIT;
13 |
--------------------------------------------------------------------------------
/admin/timescale/updates/2021-10-27-add-artist-mbids-constraint.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 | ALTER TABLE listen_mbid_mapping
3 | ADD CONSTRAINT listen_mbid_mapping_artist_mbids_check
4 | CHECK ( array_ndims(artist_mbids) = 1 );
5 | COMMIT;
6 |
--------------------------------------------------------------------------------
/admin/timescale/updates/2022-01-27-fixup-created-column.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | -- created field was not always a part of listen table. at the time it was added, it was set to 'epoch' for pre-existing
4 | -- rows and left as nullable. however, it makes more sense to set created according to listened_at field. we can also
5 | -- add the not null constraint.
6 | UPDATE listen
7 | SET created = to_timestamp(listened_at)
8 | WHERE created = 'epoch';
9 |
10 | ALTER TABLE listen ALTER COLUMN created SET NOT NULL;
11 | COMMIT;
12 |
--------------------------------------------------------------------------------
/admin/timescale/updates/2022-02-02-add-listen-table-delete-column.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 | CREATE TABLE listen_delete_metadata (
3 | id SERIAL NOT NULL,
4 | user_id INTEGER NOT NULL,
5 | listened_at BIGINT NOT NULL,
6 | recording_msid UUID NOT NULL
7 | );
8 | COMMIT;
9 |
--------------------------------------------------------------------------------
/admin/timescale/updates/2022-05-29-add-check-again-column.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 | ALTER TABLE mbid_mapping ADD COLUMN check_again TIMESTAMP WITH TIME ZONE;
3 | COMMIT;
4 |
5 | -- first time adding the column need to set check_again column to a non-null
6 | -- value so that next time the msid comes in the rechecking stuff kicks in
7 | BEGIN;
8 | UPDATE mbid_mapping SET check_again = NOW() WHERE match_type = 'no_match';
9 | COMMIT;
10 |
--------------------------------------------------------------------------------
/admin/timescale/updates/2022-10-23-add-artist-similarity.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | CREATE TABLE similarity.artist (
4 | mbid0 UUID NOT NULL,
5 | mbid1 UUID NOT NULL,
6 | metadata JSONB NOT NULL
7 | );
8 |
9 | CREATE UNIQUE INDEX similar_artists_uniq_idx ON similarity.artist (mbid0, mbid1);
10 | -- reverse index is only needed for performance reasons
11 | CREATE UNIQUE INDEX similar_artists_reverse_uniq_idx ON similarity.artist (mbid1, mbid0);
12 | CREATE INDEX similar_artists_algorithm_idx ON similarity.artist USING gin (metadata);
13 |
14 | COMMIT;
15 |
--------------------------------------------------------------------------------
/admin/timescale/updates/2022-11-08-add-additional-metadata-playlists.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE playlist.playlist RENAME COLUMN algorithm_metadata TO additional_metadata;
4 | ALTER TABLE playlist.playlist_recording ADD COLUMN additional_metadata JSONB;
5 |
6 | UPDATE playlist.playlist SET additional_metadata = jsonb_build_object('algorithm_metadata', additional_metadata);
7 |
8 | COMMIT;
9 |
--------------------------------------------------------------------------------
/admin/timescale/updates/2023-06-130-fix-none-in-recording-mbid.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | UPDATE listen l
4 | SET data = jsonb_set(l.data, '{additional_info,recording_mbid}', coalesce(to_jsonb(null::int), 'null'))
5 | WHERE data->'additional_info'->>'recording_mbid' = 'None'
6 |
7 | commit;
8 |
--------------------------------------------------------------------------------
/admin/timescale/updates/2023-09-05-update-column-names-for-spotify-metadata-cache.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE spotify_cache.album RENAME COLUMN spotify_id TO album_id;
4 | ALTER TABLE spotify_cache.artist RENAME COLUMN spotify_id TO artist_id;
5 | ALTER TABLE spotify_cache.track RENAME COLUMN spotify_id TO track_id;
6 |
7 | COMMIT;
8 |
--------------------------------------------------------------------------------
/admin/timescale/updates/2024-05-28-add-pg_trgm-extension.sql:
--------------------------------------------------------------------------------
1 | CREATE EXTENSION IF NOT EXISTS "pg_trgm";
2 | ALTER DATABASE listenbrainz_ts SET pg_trgm.word_similarity_threshold = 0.1;
3 |
--------------------------------------------------------------------------------
/admin/timescale/updates/2024-07-05-add-soundcloud-metadata-cache-2.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 |
4 | ALTER TABLE soundcloud_cache.track ADD COLUMN release_year INTEGER;
5 | ALTER TABLE soundcloud_cache.track ADD COLUMN release_month INTEGER;
6 | ALTER TABLE soundcloud_cache.track ADD COLUMN release_day INTEGER;
7 |
8 | UPDATE soundcloud_cache.track
9 | SET release_year = (data->>'release_year')::int
10 | , release_month = (data->>'release_month')::int
11 | , release_day = (data->>'release_day')::int;
12 |
13 | COMMIT;
14 |
--------------------------------------------------------------------------------
/admin/timescale/updates/2025-02-18-add-listen-delete-metadata.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | ALTER TABLE listen_delete_metadata ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE;
4 | ALTER TABLE listen_delete_metadata ADD COLUMN listen_created TIMESTAMP WITH TIME ZONE;
5 | ALTER TABLE listen_delete_metadata
6 | ADD CONSTRAINT listen_delete_metadata_deleted_created_constraint
7 | CHECK ( deleted IS FALSE OR (deleted IS TRUE AND listen_created IS NOT NULL) );
8 |
9 | COMMIT;
10 |
--------------------------------------------------------------------------------
/admin/timescale/updates/2025-02-19-add-user-listen-history-delete.sql:
--------------------------------------------------------------------------------
1 | BEGIN;
2 |
3 | CREATE TABLE deleted_user_listen_history (
4 | id INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL,
5 | user_id INTEGER NOT NULL,
6 | max_created TIMESTAMP WITH TIME ZONE NOT NULL
7 | );
8 |
9 | COMMIT;
10 |
--------------------------------------------------------------------------------
/data/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/data/__init__.py
--------------------------------------------------------------------------------
/data/model/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/data/model/__init__.py
--------------------------------------------------------------------------------
/data/model/new_releases_stat.py:
--------------------------------------------------------------------------------
1 | import pydantic
2 | from typing import List
3 |
4 | from pydantic import NonNegativeInt
5 |
6 |
7 | class NewReleasesStat(pydantic.BaseModel):
8 | type: str
9 | year: NonNegativeInt
10 | user_id: NonNegativeInt
11 | data: List
12 |
--------------------------------------------------------------------------------
/data/model/sitewide_entity.py:
--------------------------------------------------------------------------------
1 | from pydantic import constr, NonNegativeInt
2 |
3 | from data.model.common_stat_spark import StatMessage
4 | from data.model.user_entity import EntityRecord
5 |
6 |
7 | class SitewideEntityStatMessage(StatMessage[EntityRecord]):
8 | """ Format of messages sent to the ListenBrainz Server """
9 | entity: constr(min_length=1) # The entity for which stats are calculated, i.e artist, release or recording
10 | count: NonNegativeInt
11 |
--------------------------------------------------------------------------------
/data/model/user_daily_activity.py:
--------------------------------------------------------------------------------
1 | """ Models for user's daily activity statistics.
2 | The daily activity shows the number of listens submitted to ListenBrainz per hour in last week/month/year.
3 | """
4 | from pydantic import BaseModel, NonNegativeInt, constr
5 |
6 |
7 | class DailyActivityRecord(BaseModel):
8 | """ Each individual record for user's daily activity contains the time range,
9 | timestamp for start and end of the time range and listen count.
10 | """
11 | day: constr(min_length=1)
12 | hour: NonNegativeInt
13 | listen_count: NonNegativeInt
14 |
--------------------------------------------------------------------------------
/data/postgres/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/data/postgres/__init__.py
--------------------------------------------------------------------------------
/docker/consul-template.conf:
--------------------------------------------------------------------------------
1 | template {
2 | source = "/code/listenbrainz/consul_config.py.ctmpl"
3 | destination = "/code/listenbrainz/listenbrainz/config.py"
4 | }
5 | template {
6 | source = "/code/listenbrainz/admin/config.sh.ctmpl"
7 | destination = "/code/listenbrainz/admin/config.sh"
8 | }
9 |
--------------------------------------------------------------------------------
/docker/couchdb_test.ini:
--------------------------------------------------------------------------------
1 | [couchdb]
2 | single_node = true
3 |
--------------------------------------------------------------------------------
/docker/prod/nginx/nginx.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | server_name proxy.listenbrainz.org;
4 | root /usr/share/nginx/html;
5 | location / {
6 | try_files $uri @wsgi;
7 | }
8 | location @wsgi {
9 | include uwsgi_params;
10 | uwsgi_pass 10.2.2.45:13037;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/docker/services/api_compat/api_compat.finish:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export service="api-compat"
4 |
5 | . /etc/lb-startup-common.sh
6 |
7 |
8 | generate_message "$service" "$@"
9 |
10 | log "$message"
11 |
12 | send_sentry_message "$message"
13 |
14 | if [ "$1" != "0" ]; then
15 | log "Exited with non-0 status, sleeping 10 seconds"
16 | sleep 10
17 | fi
18 |
--------------------------------------------------------------------------------
/docker/services/api_compat/api_compat.service:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -x
4 |
5 | UWSGI_PID_FILE=/tmp/uwsgi-api-compat.pid
6 | if [ -e "$UWSGI_PID_FILE" ]; then
7 | UWSGI_PID=`cat "$UWSGI_PID_FILE"`
8 | echo "Previous uwsgi master process pid file detected, killing process $UWSGI_PID..."
9 | kill -TERM "$UWSGI_PID"
10 | sleep 4
11 | fi
12 |
13 | # wait for syslog to start up
14 | sleep 1
15 |
16 | exec run-consul-template -config /etc/consul-template-api-compat.conf
17 |
--------------------------------------------------------------------------------
/docker/services/api_compat/consul-template-api-compat.conf:
--------------------------------------------------------------------------------
1 | template {
2 | source = "/code/listenbrainz/consul_config.py.ctmpl"
3 | destination = "/code/listenbrainz/listenbrainz/config.py"
4 | }
5 |
6 | exec {
7 | command = ["run-lb-command", "uwsgi", "/etc/uwsgi/uwsgi-api-compat.ini"]
8 | splay = "5s"
9 | reload_signal = "SIGHUP"
10 | kill_signal = "SIGTERM"
11 | kill_timeout = "30s"
12 | }
13 |
--------------------------------------------------------------------------------
/docker/services/api_compat/uwsgi-api-compat.ini:
--------------------------------------------------------------------------------
1 | [uwsgi]
2 | uid = www-data
3 | gid = www-data
4 | master = true
5 | socket = 0.0.0.0:3031
6 | module = listenbrainz.api_compat
7 | callable = application
8 | chdir = /code/listenbrainz
9 | enable-threads = true
10 | processes = 50
11 | log-x-forwarded-for=true
12 | disable-logging = true
13 | ; quit uwsgi if the python app fails to load
14 | need-app = true
15 | die-on-term = true
16 | safe-pidfile = /tmp/uwsgi-api-compat.pid
17 |
--------------------------------------------------------------------------------
/docker/services/apple_metadata_cache/apple_metadata_cache.finish:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export service="apple-metadata-cache"
4 |
5 | . /etc/lb-startup-common.sh
6 |
7 |
8 | generate_message "$service" "$@"
9 |
10 | log "$message"
11 |
12 | send_sentry_message "$message"
13 |
14 | if [ "$1" != "0" ]; then
15 | log "Exited with non-0 status, sleeping 10 seconds"
16 | sleep 10
17 | fi
18 |
--------------------------------------------------------------------------------
/docker/services/apple_metadata_cache/apple_metadata_cache.service:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sleep 1
4 | exec run-consul-template -config /etc/consul-template-apple-metadata-cache.conf
5 |
--------------------------------------------------------------------------------
/docker/services/apple_metadata_cache/consul-template-apple-metadata-cache.conf:
--------------------------------------------------------------------------------
1 | template {
2 | source = "/code/listenbrainz/consul_config.py.ctmpl"
3 | destination = "/code/listenbrainz/listenbrainz/config.py"
4 | }
5 |
6 | exec {
7 | command = ["run-lb-command", "python3", "-u", "-m", "listenbrainz.metadata_cache.apple.runner"]
8 | splay = "60s"
9 | reload_signal = "SIGHUP"
10 | kill_signal = "SIGTERM"
11 | kill_timeout = "30s"
12 | }
13 |
--------------------------------------------------------------------------------
/docker/services/background_tasks/background_tasks.finish:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export service="background-tasks"
4 |
5 | . /etc/lb-startup-common.sh
6 |
7 |
8 | generate_message "$service" "$@"
9 |
10 | log "$message"
11 |
12 | send_sentry_message "$message"
13 |
14 | if [ "$1" != "0" ]; then
15 | log "Exited with non-0 status, sleeping 10 seconds"
16 | sleep 10
17 | fi
18 |
--------------------------------------------------------------------------------
/docker/services/background_tasks/background_tasks.service:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sleep 1
4 | exec run-consul-template -config /etc/consul-template-background-tasks.conf
5 |
--------------------------------------------------------------------------------
/docker/services/background_tasks/consul-template-background-tasks.conf:
--------------------------------------------------------------------------------
1 | template {
2 | source = "/code/listenbrainz/consul_config.py.ctmpl"
3 | destination = "/code/listenbrainz/listenbrainz/config.py"
4 | }
5 |
6 | exec {
7 | command = ["run-lb-command", "python3", "-u", "-m", "listenbrainz.background.background_tasks"]
8 | splay = "5s"
9 | reload_signal = "SIGHUP"
10 | kill_signal = "SIGTERM"
11 | kill_timeout = "30s"
12 | }
13 |
--------------------------------------------------------------------------------
/docker/services/cron/consul-template-cron-config.conf:
--------------------------------------------------------------------------------
1 | template {
2 | source = "/code/listenbrainz/consul_config.py.ctmpl"
3 | destination = "/code/listenbrainz/listenbrainz/config.py"
4 | }
5 |
6 | template {
7 | source = "/code/listenbrainz/admin/config.sh.ctmpl"
8 | destination = "/code/listenbrainz/admin/config.sh"
9 | }
10 |
--------------------------------------------------------------------------------
/docker/services/cron/cron-config.service:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sleep 1
4 | exec run-consul-template -config /etc/consul-template-cron-config.conf
5 |
--------------------------------------------------------------------------------
/docker/services/labs_api/consul-template-labs-api.conf:
--------------------------------------------------------------------------------
1 | template {
2 | source = "/code/listenbrainz/consul_config.py.ctmpl"
3 | destination = "/code/listenbrainz/listenbrainz/config.py"
4 | }
5 |
6 | exec {
7 | command = ["run-lb-command", "uwsgi", "/etc/uwsgi/uwsgi-labs-api.ini"]
8 | splay = "5s"
9 | reload_signal = "SIGHUP"
10 | kill_signal = "SIGTERM"
11 | kill_timeout = "30s"
12 | }
13 |
--------------------------------------------------------------------------------
/docker/services/labs_api/labs_api.finish:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export service="labs-api"
4 |
5 | . /etc/lb-startup-common.sh
6 |
7 |
8 | generate_message "$service" "$@"
9 |
10 | log "$message"
11 |
12 | send_sentry_message "$message"
13 |
14 | if [ "$1" != "0" ]; then
15 | log "Exited with non-0 status, sleeping 10 seconds"
16 | sleep 10
17 | fi
18 |
--------------------------------------------------------------------------------
/docker/services/labs_api/labs_api.service:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -x
4 |
5 | UWSGI_PID_FILE=/tmp/uwsgi-labs-api.pid
6 | if [ -e "$UWSGI_PID_FILE" ]; then
7 | UWSGI_PID=`cat "$UWSGI_PID_FILE"`
8 | echo "Previous uwsgi master process pid file detected, killing process $UWSGI_PID..."
9 | kill -TERM "$UWSGI_PID"
10 | sleep 4
11 | fi
12 |
13 | # wait for syslog to start up
14 | sleep 1
15 |
16 | exec run-consul-template -config /etc/consul-template-labs-api.conf
17 |
--------------------------------------------------------------------------------
/docker/services/labs_api/uwsgi-labs-api.ini:
--------------------------------------------------------------------------------
1 | [uwsgi]
2 | uid = www-data
3 | gid = www-data
4 | master = true
5 | socket = 0.0.0.0:3031
6 | module = listenbrainz.labs_api.labs.main
7 | callable = app
8 | chdir = /code/listenbrainz
9 | enable-threads = true
10 | processes = 10
11 | log-x-forwarded-for=true
12 | disable-logging = true
13 | ; quit uwsgi if the python app fails to load
14 | need-app = true
15 | ; increase buffer size for requests that send a lot of mbids in query params
16 | buffer-size = 8192
17 | die-on-term = true
18 | safe-pidfile = /tmp/uwsgi-labs-api.pid
19 |
--------------------------------------------------------------------------------
/docker/services/lastfm_importer/consul-template-lastfm-importer.conf:
--------------------------------------------------------------------------------
1 | template {
2 | source = "/code/listenbrainz/consul_config.py.ctmpl"
3 | destination = "/code/listenbrainz/listenbrainz/config.py"
4 | }
5 |
6 | exec {
7 | command = ["run-lb-command", "python3", "-m", "listenbrainz.listens_importer.lastfm"]
8 | splay = "5s"
9 | reload_signal = "SIGHUP"
10 | kill_signal = "SIGTERM"
11 | kill_timeout = "30s"
12 | }
13 |
--------------------------------------------------------------------------------
/docker/services/lastfm_importer/lastfm_importer.finish:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export service="lastfm-importer"
4 |
5 | . /etc/lb-startup-common.sh
6 |
7 |
8 | generate_message "$service" "$@"
9 |
10 | log "$message"
11 |
12 | send_sentry_message "$message"
13 |
14 | if [ "$1" != "0" ]; then
15 | log "Exited with non-0 status, sleeping 10 seconds"
16 | sleep 10
17 | fi
18 |
--------------------------------------------------------------------------------
/docker/services/lastfm_importer/lastfm_importer.service:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sleep 1
4 | exec run-consul-template -config /etc/consul-template-lastfm-importer.conf
5 |
--------------------------------------------------------------------------------
/docker/services/mbid_mapping_writer/consul-template-mbid-mapping-writer.conf:
--------------------------------------------------------------------------------
1 | template {
2 | source = "/code/listenbrainz/consul_config.py.ctmpl"
3 | destination = "/code/listenbrainz/listenbrainz/config.py"
4 | }
5 |
6 | exec {
7 | command = ["run-lb-command", "python3", "-u", "-m", "listenbrainz.mbid_mapping_writer.mbid_mapping_writer"]
8 | splay = "60s"
9 | reload_signal = "SIGHUP"
10 | kill_signal = "SIGTERM"
11 | kill_timeout = "30s"
12 | }
13 |
--------------------------------------------------------------------------------
/docker/services/mbid_mapping_writer/mbid_mapping_writer.finish:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export service="mbid-mapping-writer"
4 |
5 | . /etc/lb-startup-common.sh
6 |
7 |
8 | generate_message "$service" "$@"
9 |
10 | log "$message"
11 |
12 | send_sentry_message "$message"
13 |
14 | if [ "$1" != "0" ]; then
15 | log "Exited with non-0 status, sleeping 10 seconds"
16 | sleep 10
17 | fi
18 |
--------------------------------------------------------------------------------
/docker/services/mbid_mapping_writer/mbid_mapping_writer.service:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sleep 1
4 | exec run-consul-template -config /etc/consul-template-mbid-mapping-writer.conf
5 |
--------------------------------------------------------------------------------
/docker/services/soundcloud_metadata_cache/consul-template-soundcloud-metadata-cache.conf:
--------------------------------------------------------------------------------
1 | template {
2 | source = "/code/listenbrainz/consul_config.py.ctmpl"
3 | destination = "/code/listenbrainz/listenbrainz/config.py"
4 | }
5 |
6 | exec {
7 | command = ["run-lb-command", "python3", "-u", "-m", "listenbrainz.metadata_cache.soundcloud.runner"]
8 | splay = "60s"
9 | reload_signal = "SIGHUP"
10 | kill_signal = "SIGTERM"
11 | kill_timeout = "30s"
12 | }
13 |
--------------------------------------------------------------------------------
/docker/services/soundcloud_metadata_cache/soundcloud_metadata_cache.finish:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export service="soundcloud-metadata-cache"
4 |
5 | . /etc/lb-startup-common.sh
6 |
7 |
8 | generate_message "$service" "$@"
9 |
10 | log "$message"
11 |
12 | send_sentry_message "$message"
13 |
14 | if [ "$1" != "0" ]; then
15 | log "Exited with non-0 status, sleeping 10 seconds"
16 | sleep 10
17 | fi
18 |
--------------------------------------------------------------------------------
/docker/services/soundcloud_metadata_cache/soundcloud_metadata_cache.service:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sleep 1
4 | exec run-consul-template -config /etc/consul-template-soundcloud-metadata-cache.conf
5 |
--------------------------------------------------------------------------------
/docker/services/spark_reader/consul-template-spark-reader.conf:
--------------------------------------------------------------------------------
1 | template {
2 | source = "/code/listenbrainz/consul_config.py.ctmpl"
3 | destination = "/code/listenbrainz/listenbrainz/config.py"
4 | }
5 |
6 | exec {
7 | command = ["run-lb-command", "python3", "-m", "listenbrainz.spark.spark_reader"]
8 | splay = "5s"
9 | reload_signal = "SIGHUP"
10 | kill_signal = "SIGTERM"
11 | kill_timeout = "30s"
12 | }
13 |
--------------------------------------------------------------------------------
/docker/services/spark_reader/spark_reader.finish:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export service="spark-reader"
4 |
5 | . /etc/lb-startup-common.sh
6 |
7 |
8 | generate_message "$service" "$@"
9 |
10 | log "$message"
11 |
12 | send_sentry_message "$message"
13 |
14 | if [ "$1" != "0" ]; then
15 | log "Exited with non-0 status, sleeping 10 seconds"
16 | sleep 10
17 | fi
18 |
--------------------------------------------------------------------------------
/docker/services/spark_reader/spark_reader.service:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sleep 1
4 | exec run-consul-template -config /etc/consul-template-spark-reader.conf
5 |
--------------------------------------------------------------------------------
/docker/services/spotify_metadata_cache/consul-template-spotify-metadata-cache.conf:
--------------------------------------------------------------------------------
1 | template {
2 | source = "/code/listenbrainz/consul_config.py.ctmpl"
3 | destination = "/code/listenbrainz/listenbrainz/config.py"
4 | }
5 |
6 | exec {
7 | command = ["run-lb-command", "python3", "-u", "-m", "listenbrainz.metadata_cache.spotify.runner"]
8 | splay = "60s"
9 | reload_signal = "SIGHUP"
10 | kill_signal = "SIGTERM"
11 | kill_timeout = "30s"
12 | }
13 |
--------------------------------------------------------------------------------
/docker/services/spotify_metadata_cache/spotify_metadata_cache.finish:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export service="spotify-metadata-cache"
4 |
5 | . /etc/lb-startup-common.sh
6 |
7 |
8 | generate_message "$service" "$@"
9 |
10 | log "$message"
11 |
12 | send_sentry_message "$message"
13 |
14 | if [ "$1" != "0" ]; then
15 | log "Exited with non-0 status, sleeping 10 seconds"
16 | sleep 10
17 | fi
18 |
--------------------------------------------------------------------------------
/docker/services/spotify_metadata_cache/spotify_metadata_cache.service:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sleep 1
4 | exec run-consul-template -config /etc/consul-template-spotify-metadata-cache.conf
5 |
--------------------------------------------------------------------------------
/docker/services/spotify_reader/consul-template-spotify-reader.conf:
--------------------------------------------------------------------------------
1 | template {
2 | source = "/code/listenbrainz/consul_config.py.ctmpl"
3 | destination = "/code/listenbrainz/listenbrainz/config.py"
4 | }
5 |
6 | exec {
7 | command = ["run-lb-command", "python3", "-m", "listenbrainz.listens_importer.spotify"]
8 | splay = "5s"
9 | reload_signal = "SIGHUP"
10 | kill_signal = "SIGTERM"
11 | kill_timeout = "30s"
12 | }
13 |
--------------------------------------------------------------------------------
/docker/services/spotify_reader/spotify_reader.finish:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export service="spotify-reader"
4 |
5 | . /etc/lb-startup-common.sh
6 |
7 |
8 | generate_message "$service" "$@"
9 |
10 | log "$message"
11 |
12 | send_sentry_message "$message"
13 |
14 | if [ "$1" != "0" ]; then
15 | log "Exited with non-0 status, sleeping 10 seconds"
16 | sleep 10
17 | fi
18 |
--------------------------------------------------------------------------------
/docker/services/spotify_reader/spotify_reader.service:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sleep 1
4 | exec run-consul-template -config /etc/consul-template-spotify-reader.conf
5 |
--------------------------------------------------------------------------------
/docker/services/timescale_writer/consul-template-timescale-writer.conf:
--------------------------------------------------------------------------------
1 | template {
2 | source = "/code/listenbrainz/consul_config.py.ctmpl"
3 | destination = "/code/listenbrainz/listenbrainz/config.py"
4 | }
5 |
6 | exec {
7 | command = ["run-lb-command", "python3", "-u", "-m", "listenbrainz.timescale_writer.timescale_writer"]
8 | splay = "5s"
9 | reload_signal = "SIGHUP"
10 | kill_signal = "SIGTERM"
11 | kill_timeout = "30s"
12 | }
13 |
--------------------------------------------------------------------------------
/docker/services/timescale_writer/timescale_writer.finish:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export service="timescale-writer"
4 |
5 | . /etc/lb-startup-common.sh
6 |
7 |
8 | generate_message "$service" "$@"
9 |
10 | log "$message"
11 |
12 | send_sentry_message "$message"
13 |
14 | if [ "$1" != "0" ]; then
15 | log "Exited with non-0 status, sleeping 10 seconds"
16 | sleep 10
17 | fi
18 |
--------------------------------------------------------------------------------
/docker/services/timescale_writer/timescale_writer.service:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sleep 1
4 | exec run-consul-template -config /etc/consul-template-timescale-writer.conf
5 |
--------------------------------------------------------------------------------
/docker/services/uwsgi/consul-template-uwsgi.conf:
--------------------------------------------------------------------------------
1 | template {
2 | source = "/etc/uwsgi/uwsgi.ini.ctmpl"
3 | destination = "/etc/uwsgi/uwsgi.ini"
4 | command = "sv hup uwsgi"
5 | }
6 |
7 | template {
8 | source = "/code/listenbrainz/consul_config.py.ctmpl"
9 | destination = "/code/listenbrainz/listenbrainz/config.py"
10 | }
11 |
12 | exec {
13 | command = ["run-lb-command", "uwsgi", "/etc/uwsgi/uwsgi.ini"]
14 | splay = "5s"
15 | reload_signal = "SIGHUP"
16 | kill_signal = "SIGTERM"
17 | kill_timeout = "30s"
18 | }
19 |
--------------------------------------------------------------------------------
/docker/services/uwsgi/uwsgi.finish:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export service="uwsgi"
4 |
5 | . /etc/lb-startup-common.sh
6 |
7 |
8 | generate_message "$service" "$@"
9 |
10 | log "$message"
11 |
12 | send_sentry_message "$message"
13 |
14 | if [ "$1" != "0" ]; then
15 | log "Exited with non-0 status, sleeping 10 seconds"
16 | sleep 10
17 | fi
18 |
--------------------------------------------------------------------------------
/docker/services/uwsgi/uwsgi.service:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -x
4 |
5 | UWSGI_PID_FILE=/tmp/uwsgi.pid
6 | if [ -e "$UWSGI_PID_FILE" ]; then
7 | UWSGI_PID=`cat "$UWSGI_PID_FILE"`
8 | echo "Previous uwsgi master process pid file detected, killing process $UWSGI_PID..."
9 | kill -TERM "$UWSGI_PID"
10 | sleep 4
11 | fi
12 |
13 | # wait for syslog to start up
14 | sleep 1
15 |
16 | exec run-consul-template -config /etc/consul-template-uwsgi.conf
17 |
--------------------------------------------------------------------------------
/docker/services/websockets/consul-template-websockets.conf:
--------------------------------------------------------------------------------
1 | template {
2 | source = "/code/listenbrainz/consul_config.py.ctmpl"
3 | destination = "/code/listenbrainz/listenbrainz/config.py"
4 | }
5 |
6 | exec {
7 | command = ["run-lb-command", "python3", "manage.py", "run_websockets", "-h", "0.0.0.0", "-p", "3031"]
8 | splay = "5s"
9 | reload_signal = "SIGHUP"
10 | kill_signal = "SIGTERM"
11 | kill_timeout = "30s"
12 | }
13 |
--------------------------------------------------------------------------------
/docker/services/websockets/websockets.finish:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export service="websockets"
4 |
5 | . /etc/lb-startup-common.sh
6 |
7 |
8 | generate_message "$service" "$@"
9 |
10 | log "$message"
11 |
12 | send_sentry_message "$message"
13 |
14 | if [ "$1" != "0" ]; then
15 | log "Exited with non-0 status, sleeping 10 seconds"
16 | sleep 10
17 | fi
18 |
--------------------------------------------------------------------------------
/docker/services/websockets/websockets.service:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sleep 1
4 | exec run-consul-template -config /etc/consul-template-websockets.conf
5 |
--------------------------------------------------------------------------------
/docker/spark-dev-push.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Build base metabrainz spark image (only used in development and testing)
4 | # and push it to Docker Hub, with an optional tag (which defaults to "latest")
5 | #
6 | # Usage:
7 | # $ ./spark-dev-push.sh [tag]
8 |
9 | set -e
10 |
11 | cd "$(dirname "${BASH_SOURCE[0]}")"
12 |
13 | TAG=${1:-latest}
14 |
15 | docker build --tag metabrainz/listenbrainz-spark-base:"$TAG" -f Dockerfile.spark.base . \
16 | && docker push metabrainz/listenbrainz-spark-base
17 |
--------------------------------------------------------------------------------
/docker/start-labs-api.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | docker-compose -f docker/docker-compose.labs.api.yml -p listenbrainz "$@"
4 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # Sphinx stuff
2 | _build
3 | build
4 | # virtualenv for doc stuff
5 | ve
6 | _templates
7 |
--------------------------------------------------------------------------------
/docs/images/auth-popup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/docs/images/auth-popup.png
--------------------------------------------------------------------------------
/docs/images/dataflows-graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/docs/images/dataflows-graph.png
--------------------------------------------------------------------------------
/docs/images/release-result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/docs/images/release-result.png
--------------------------------------------------------------------------------
/docs/images/release-workflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/docs/images/release-workflow.png
--------------------------------------------------------------------------------
/docs/images/request_consumer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/docs/images/request_consumer.png
--------------------------------------------------------------------------------
/docs/images/user-profile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/docs/images/user-profile.png
--------------------------------------------------------------------------------
/docs/maintainers/deploy.rst:
--------------------------------------------------------------------------------
1 | Production Deployment
2 | =====================
3 |
4 | .. note::
5 |
6 | This documentation is for ListenBrainz maintainers for when they deploy the website
7 |
8 | Cron
9 | ^^^^
10 |
11 | You can cleanly shut down cron from ``docker-server-configs`` by running
12 |
13 | .. code-block:: bash
14 |
15 | ./scripts/terminate_lb_cron.sh
16 |
17 | If no cron jobs are running, this will stop and delete the cron container. If a job is running
18 | it will notify you and not stop the container.
19 |
--------------------------------------------------------------------------------
/docs/maintainers/pull-requests.rst:
--------------------------------------------------------------------------------
1 | Pull Requests Policy
2 | ====================
3 |
4 | It is recommended that maintainers (unless the change is urgently needed) do not push directly or merge pull requests
5 | without review . By default, **one** approving review is sufficient to merge a pull request. The pull request author
6 | or the reviewer can request more reviews or review from a specific person as they deem necessary.
7 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | Sphinx==7.2.6
2 | sphinxcontrib-httpdomain==1.8.1
3 | sphinx_rtd_theme==2.0.0
4 | docutils==0.20.1
5 | sphinx-click==5.1.0
6 | -r ../requirements.txt
7 |
--------------------------------------------------------------------------------
/docs/users/api/metadata.rst:
--------------------------------------------------------------------------------
1 | .. _metadata-api:
2 |
3 | Metadata
4 | ========
5 |
6 | The metadata API looks up MusicBrainz metadata for recordings
7 |
8 | .. autoflask:: listenbrainz.webserver:create_app_rtfd()
9 | :blueprints: metadata
10 | :include-empty-docstring:
11 | :undoc-static:
12 |
--------------------------------------------------------------------------------
/docs/users/api/playlist.rst:
--------------------------------------------------------------------------------
1 | Playlists
2 | =========
3 |
4 | The playlists API allows for the creation and editing of lists of recordings
5 |
6 | .. autoflask:: listenbrainz.webserver:create_app_rtfd()
7 | :endpoints: api_v1.get_playlists_for_user, api_v1.get_playlists_created_for_user, api_v1.get_playlists_collaborated_on_for_user
8 | :include-empty-docstring:
9 | :undoc-static:
10 |
11 | .. autoflask:: listenbrainz.webserver:create_app_rtfd()
12 | :blueprints: playlist_api_v1
13 | :include-empty-docstring:
14 | :undoc-static:
15 |
--------------------------------------------------------------------------------
/docs/users/api/popularity.rst:
--------------------------------------------------------------------------------
1 | .. _popularity-api:
2 |
3 | Popularity
4 | ==========
5 |
6 | The popularity APIs return the total listen and listeners count for various entities and also a way to query top entities
7 | for a given artist.
8 |
9 | .. autoflask:: listenbrainz.webserver:create_app_rtfd()
10 | :blueprints: popularity_api_v1
11 | :include-empty-docstring:
12 | :undoc-static:
13 |
--------------------------------------------------------------------------------
/docs/users/api/settings.rst:
--------------------------------------------------------------------------------
1 | Settings
2 | ========
3 |
4 | These endpoints allow to customize a user's preferences/settings.
5 |
6 | .. autoflask:: listenbrainz.webserver:create_app_rtfd()
7 | :blueprints: user_settings_api_v1
8 | :include-empty-docstring:
9 | :undoc-static:
10 |
--------------------------------------------------------------------------------
/docs/users/api/social.rst:
--------------------------------------------------------------------------------
1 | Social
2 | ======
3 |
4 | User Timeline API
5 | ^^^^^^^^^^^^^^^^^
6 |
7 | These api endpoints allow to create and fetch timeline events for a user.
8 |
9 | .. autoflask:: listenbrainz.webserver:create_app_rtfd()
10 | :blueprints: user_timeline_event_api_bp
11 |
12 | Follow API
13 | ^^^^^^^^^^
14 |
15 | These apis allow to interact with follow user feature of ListenBrainz.
16 |
17 | .. autoflask:: listenbrainz.webserver:create_app_rtfd()
18 | :blueprints: social_api_v1
19 | :include-empty-docstring:
20 | :undoc-static:
21 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | !js/*
2 | dist/
--------------------------------------------------------------------------------
/frontend/css/.gitignore:
--------------------------------------------------------------------------------
1 | *.css
2 | !widgets.css
--------------------------------------------------------------------------------
/frontend/css/colors.less:
--------------------------------------------------------------------------------
1 | // ListenBrainz logo colors
2 | @blue: #353070;
3 | @orange: #eb743b;
4 | @white: #ffffff;
5 | @light: #c0cfb2;
6 | @light-grey: #8d8d8d;
7 | @dark: #053b47;
8 |
--------------------------------------------------------------------------------
/frontend/css/messybrainz.less:
--------------------------------------------------------------------------------
1 | .messybrainz-header {
2 | margin-top: 40px;
3 | img {
4 | display: block;
5 | margin: 0 auto;
6 | height: 80px;
7 | }
8 | margin-bottom: 40px;
9 | }
10 |
--------------------------------------------------------------------------------
/frontend/css/personal-recommendation-modal.less:
--------------------------------------------------------------------------------
1 | @white-background: #fff;
2 |
3 | #PersonalRecommendationModal {
4 | p {
5 | margin-top: 24px;
6 | margin-bottom: 24px;
7 | }
8 |
9 | .character-count {
10 | display: block;
11 | text-align: right;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/frontend/css/theme/bootstrap/mixins/alerts.less:
--------------------------------------------------------------------------------
1 | // Alerts
2 |
3 | .alert-variant(@background; @border; @text-color) {
4 | background-color: @background;
5 | border-color: @border;
6 | color: @text-color;
7 |
8 | hr {
9 | border-top-color: darken(@border, 5%);
10 | }
11 | .alert-link {
12 | color: darken(@text-color, 10%);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/frontend/css/theme/bootstrap/mixins/background-variant.less:
--------------------------------------------------------------------------------
1 | // Contextual backgrounds
2 |
3 | .bg-variant(@color) {
4 | background-color: @color;
5 | a&:hover,
6 | a&:focus {
7 | background-color: darken(@color, 10%);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/frontend/css/theme/bootstrap/mixins/center-block.less:
--------------------------------------------------------------------------------
1 | // Center-align a block level element
2 |
3 | .center-block() {
4 | display: block;
5 | margin-left: auto;
6 | margin-right: auto;
7 | }
8 |
--------------------------------------------------------------------------------
/frontend/css/theme/bootstrap/mixins/labels.less:
--------------------------------------------------------------------------------
1 | // Labels
2 |
3 | .label-variant(@color) {
4 | background-color: @color;
5 |
6 | &[href] {
7 | &:hover,
8 | &:focus {
9 | background-color: darken(@color, 10%);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/frontend/css/theme/bootstrap/mixins/nav-divider.less:
--------------------------------------------------------------------------------
1 | // Horizontal dividers
2 | //
3 | // Dividers (basically an hr) within dropdowns and nav lists
4 |
5 | .nav-divider(@color: #e5e5e5) {
6 | height: 1px;
7 | margin: ((@line-height-computed / 2) - 1) 0;
8 | overflow: hidden;
9 | background-color: @color;
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/css/theme/bootstrap/mixins/nav-vertical-align.less:
--------------------------------------------------------------------------------
1 | // Navbar vertical align
2 | //
3 | // Vertically center elements in the navbar.
4 | // Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.
5 |
6 | .navbar-vertical-align(@element-height) {
7 | margin-top: ((@navbar-height - @element-height) / 2);
8 | margin-bottom: ((@navbar-height - @element-height) / 2);
9 | }
10 |
--------------------------------------------------------------------------------
/frontend/css/theme/bootstrap/mixins/opacity.less:
--------------------------------------------------------------------------------
1 | // Opacity
2 |
3 | .opacity(@opacity) {
4 | opacity: @opacity;
5 | // IE8 filter
6 | @opacity-ie: (@opacity * 100);
7 | filter: ~"alpha(opacity=@{opacity-ie})";
8 | }
9 |
--------------------------------------------------------------------------------
/frontend/css/theme/bootstrap/mixins/progress-bar.less:
--------------------------------------------------------------------------------
1 | // Progress bars
2 |
3 | .progress-bar-variant(@color) {
4 | background-color: @color;
5 |
6 | // Deprecated parent class requirement as of v3.2.0
7 | .progress-striped & {
8 | #gradient > .striped();
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/css/theme/bootstrap/mixins/reset-filter.less:
--------------------------------------------------------------------------------
1 | // Reset filters for IE
2 | //
3 | // When you need to remove a gradient background, do not forget to use this to reset
4 | // the IE filter for IE9 and below.
5 |
6 | .reset-filter() {
7 | filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)"));
8 | }
9 |
--------------------------------------------------------------------------------
/frontend/css/theme/bootstrap/mixins/resize.less:
--------------------------------------------------------------------------------
1 | // Resize anything
2 |
3 | .resizable(@direction) {
4 | resize: @direction; // Options: horizontal, vertical, both
5 | overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible`
6 | }
7 |
--------------------------------------------------------------------------------
/frontend/css/theme/bootstrap/mixins/responsive-visibility.less:
--------------------------------------------------------------------------------
1 | // Responsive utilities
2 |
3 | //
4 | // More easily include all the states for responsive-utilities.less.
5 | .responsive-visibility() {
6 | display: block !important;
7 | table& { display: table !important; }
8 | tr& { display: table-row !important; }
9 | th&,
10 | td& { display: table-cell !important; }
11 | }
12 |
13 | .responsive-invisibility() {
14 | display: none !important;
15 | }
16 |
--------------------------------------------------------------------------------
/frontend/css/theme/bootstrap/mixins/size.less:
--------------------------------------------------------------------------------
1 | // Sizing shortcuts
2 |
3 | .size(@width; @height) {
4 | width: @width;
5 | height: @height;
6 | }
7 |
8 | .square(@size) {
9 | .size(@size; @size);
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/css/theme/bootstrap/mixins/tab-focus.less:
--------------------------------------------------------------------------------
1 | // WebKit-style focus
2 |
3 | .tab-focus() {
4 | // Default
5 | outline: thin dotted;
6 | // WebKit
7 | outline: 5px auto -webkit-focus-ring-color;
8 | outline-offset: -2px;
9 | }
10 |
--------------------------------------------------------------------------------
/frontend/css/theme/bootstrap/mixins/text-emphasis.less:
--------------------------------------------------------------------------------
1 | // Typography
2 |
3 | .text-emphasis-variant(@color) {
4 | color: @color;
5 | a&:hover,
6 | a&:focus {
7 | color: darken(@color, 10%);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/frontend/css/theme/bootstrap/mixins/text-overflow.less:
--------------------------------------------------------------------------------
1 | // Text overflow
2 | // Requires inline-block or block for proper styling
3 |
4 | .text-overflow() {
5 | overflow: hidden;
6 | text-overflow: ellipsis;
7 | white-space: nowrap;
8 | }
9 |
--------------------------------------------------------------------------------
/frontend/fonts/Inter.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/fonts/Inter.woff2
--------------------------------------------------------------------------------
/frontend/img/ListenBrainz_logo_no_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/ListenBrainz_logo_no_text.png
--------------------------------------------------------------------------------
/frontend/img/art/cover-art-on-floor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/art/cover-art-on-floor.png
--------------------------------------------------------------------------------
/frontend/img/art/yim-2022-shareable-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/art/yim-2022-shareable-bg.png
--------------------------------------------------------------------------------
/frontend/img/art/yim-2022-shareable-flames.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/art/yim-2022-shareable-flames.png
--------------------------------------------------------------------------------
/frontend/img/art/yim-2022-shareable-magnify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/art/yim-2022-shareable-magnify.png
--------------------------------------------------------------------------------
/frontend/img/art/yim-2022-shareable-stereo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/art/yim-2022-shareable-stereo.png
--------------------------------------------------------------------------------
/frontend/img/broken-cd.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/broken-cd.jpg
--------------------------------------------------------------------------------
/frontend/img/cover-art-placeholder.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/cover-art-placeholder.jpg
--------------------------------------------------------------------------------
/frontend/img/explore/cover-art-collage.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/cover-art-collage.jpg
--------------------------------------------------------------------------------
/frontend/img/explore/fresh-releases.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/fresh-releases.jpg
--------------------------------------------------------------------------------
/frontend/img/explore/huesound.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/huesound.jpg
--------------------------------------------------------------------------------
/frontend/img/explore/lb-radio-beta.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/lb-radio-beta.jpg
--------------------------------------------------------------------------------
/frontend/img/explore/link-listens.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/link-listens.jpg
--------------------------------------------------------------------------------
/frontend/img/explore/made-with-postgres.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/made-with-postgres.png
--------------------------------------------------------------------------------
/frontend/img/explore/music-neighborhood.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/music-neighborhood.jpg
--------------------------------------------------------------------------------
/frontend/img/explore/similar-users.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/similar-users.jpg
--------------------------------------------------------------------------------
/frontend/img/explore/stats-art-beta.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/stats-art-beta.jpg
--------------------------------------------------------------------------------
/frontend/img/explore/stats-art/template-designer-top-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/stats-art/template-designer-top-10.png
--------------------------------------------------------------------------------
/frontend/img/explore/stats-art/template-designer-top-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/stats-art/template-designer-top-5.png
--------------------------------------------------------------------------------
/frontend/img/explore/stats-art/template-grid-stats-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/stats-art/template-grid-stats-2.png
--------------------------------------------------------------------------------
/frontend/img/explore/stats-art/template-grid-stats.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/stats-art/template-grid-stats.png
--------------------------------------------------------------------------------
/frontend/img/explore/stats-art/template-lps-on-the-floor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/stats-art/template-lps-on-the-floor.png
--------------------------------------------------------------------------------
/frontend/img/explore/year-in-music-2021.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/year-in-music-2021.jpg
--------------------------------------------------------------------------------
/frontend/img/explore/year-in-music-2022.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/year-in-music-2022.jpg
--------------------------------------------------------------------------------
/frontend/img/explore/year-in-music-2023.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/year-in-music-2023.jpg
--------------------------------------------------------------------------------
/frontend/img/explore/year-in-music-2024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/explore/year-in-music-2024.png
--------------------------------------------------------------------------------
/frontend/img/favicon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/favicon-16.png
--------------------------------------------------------------------------------
/frontend/img/favicon-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/favicon-256.png
--------------------------------------------------------------------------------
/frontend/img/favicon-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/favicon-32.png
--------------------------------------------------------------------------------
/frontend/img/homepage/LB-Data-Provider-Info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/homepage/LB-Data-Provider-Info.png
--------------------------------------------------------------------------------
/frontend/img/homepage/LB-Data-Provider.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/homepage/LB-Data-Provider.png
--------------------------------------------------------------------------------
/frontend/img/homepage/LB-Ethical-Info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/homepage/LB-Ethical-Info.png
--------------------------------------------------------------------------------
/frontend/img/homepage/LB-Ethical.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/homepage/LB-Ethical.png
--------------------------------------------------------------------------------
/frontend/img/homepage/LB-Headphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/homepage/LB-Headphone.png
--------------------------------------------------------------------------------
/frontend/img/homepage/LB-Open-Source-Info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/homepage/LB-Open-Source-Info.png
--------------------------------------------------------------------------------
/frontend/img/homepage/LB-Open-Source.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/homepage/LB-Open-Source.png
--------------------------------------------------------------------------------
/frontend/img/homepage/LB-Speaker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/homepage/LB-Speaker.png
--------------------------------------------------------------------------------
/frontend/img/icons/angle_double_right_icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/img/listenbrainz-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/listenbrainz-logo.png
--------------------------------------------------------------------------------
/frontend/img/listenbrainz_logo_icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/img/playlist-cover-art/cover-art_1-0.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/frontend/img/recommendations/no-freshness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/recommendations/no-freshness.png
--------------------------------------------------------------------------------
/frontend/img/selfie.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/selfie.jpg
--------------------------------------------------------------------------------
/frontend/img/share-header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/share-header.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-2021.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-2021.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-22/buddy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-22/buddy.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-22/magnify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-22/magnify.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-22/map.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-22/map.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-22/stereo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-22/stereo.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-22/yim-22-logo-small-compressed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-22/yim-22-logo-small-compressed.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-22/yim22-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-22/yim22-logo.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/buddies-square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/buddies-square.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/cat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/cat.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/dog-tall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/dog-tall.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/dog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/dog.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/fish.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/fish.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/flower.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/flower.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/ghost-square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/ghost-square.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/heart-square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/heart-square.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/heart.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/peep-square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/peep-square.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/peep.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/peep.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/pick-color.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/pick-color.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/shareable-image-arrows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/shareable-image-arrows.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/shareable-image-hug.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/shareable-image-hug.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/trunk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/trunk.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/worm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/worm.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/yim-23-header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/yim-23-header.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/yim-23-logo-small-compressed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/yim-23-logo-small-compressed.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-23/yim23-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-23/yim23-logo.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-01.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-02.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-03.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-04.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-05.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-06.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-07.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-08.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-09.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-09.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/buddies/yim24-buddy-10.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/playlists/yim24-playlist-01-no-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/playlists/yim24-playlist-01-no-bg.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/playlists/yim24-playlist-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/playlists/yim24-playlist-01.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/playlists/yim24-playlist-02-no-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/playlists/yim24-playlist-02-no-bg.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/playlists/yim24-playlist-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/playlists/yim24-playlist-02.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/playlists/yim24-playlist-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/playlists/yim24-playlist-03.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/playlists/yim24-playlist-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/playlists/yim24-playlist-04.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/playlists/yim24-playlist-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/playlists/yim24-playlist-05.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/playlists/yim24-playlist-06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/playlists/yim24-playlist-06.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/yim24-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/yim24-01.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/yim24-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/yim24-02.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/yim24-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/yim24-03.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/autumn/yim24-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/autumn/yim24-04.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-01.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-02.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-03.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-04.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-05.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-06.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-07.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-08.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-09.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/buddies/yim24-buddy-09.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/playlists/yim24-playlist-01-no-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/playlists/yim24-playlist-01-no-bg.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/playlists/yim24-playlist-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/playlists/yim24-playlist-01.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/playlists/yim24-playlist-02-no-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/playlists/yim24-playlist-02-no-bg.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/playlists/yim24-playlist-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/playlists/yim24-playlist-02.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/playlists/yim24-playlist-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/playlists/yim24-playlist-03.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/playlists/yim24-playlist-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/playlists/yim24-playlist-04.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/playlists/yim24-playlist-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/playlists/yim24-playlist-05.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/playlists/yim24-playlist-06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/playlists/yim24-playlist-06.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/yim24-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/yim24-01.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/yim24-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/yim24-02.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/yim24-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/yim24-03.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/spring/yim24-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/spring/yim24-04.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/buddies/yim24-buddy-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/buddies/yim24-buddy-01.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/buddies/yim24-buddy-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/buddies/yim24-buddy-02.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/buddies/yim24-buddy-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/buddies/yim24-buddy-03.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/buddies/yim24-buddy-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/buddies/yim24-buddy-04.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/buddies/yim24-buddy-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/buddies/yim24-buddy-05.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/buddies/yim24-buddy-06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/buddies/yim24-buddy-06.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/buddies/yim24-buddy-07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/buddies/yim24-buddy-07.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/playlists/yim24-playlist-01-no-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/playlists/yim24-playlist-01-no-bg.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/playlists/yim24-playlist-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/playlists/yim24-playlist-01.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/playlists/yim24-playlist-02-no-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/playlists/yim24-playlist-02-no-bg.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/playlists/yim24-playlist-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/playlists/yim24-playlist-02.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/playlists/yim24-playlist-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/playlists/yim24-playlist-03.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/playlists/yim24-playlist-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/playlists/yim24-playlist-04.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/playlists/yim24-playlist-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/playlists/yim24-playlist-05.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/playlists/yim24-playlist-06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/playlists/yim24-playlist-06.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/yim24-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/yim24-01.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/yim24-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/yim24-02.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/yim24-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/yim24-03.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/summer/yim24-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/summer/yim24-04.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/winter/buddies/yim24-buddy-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/winter/buddies/yim24-buddy-01.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/winter/buddies/yim24-buddy-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/winter/buddies/yim24-buddy-02.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/winter/buddies/yim24-buddy-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/winter/buddies/yim24-buddy-03.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/winter/buddies/yim24-buddy-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/winter/buddies/yim24-buddy-04.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/winter/buddies/yim24-buddy-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/winter/buddies/yim24-buddy-05.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/winter/playlists/yim24-playlist-01-no-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/winter/playlists/yim24-playlist-01-no-bg.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/winter/playlists/yim24-playlist-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/winter/playlists/yim24-playlist-01.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/winter/playlists/yim24-playlist-02-no-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/winter/playlists/yim24-playlist-02-no-bg.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/winter/playlists/yim24-playlist-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/winter/playlists/yim24-playlist-02.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/winter/playlists/yim24-playlist-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/winter/playlists/yim24-playlist-03.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/winter/playlists/yim24-playlist-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/winter/playlists/yim24-playlist-04.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/winter/playlists/yim24-playlist-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/winter/playlists/yim24-playlist-05.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/winter/playlists/yim24-playlist-06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/winter/playlists/yim24-playlist-06.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/winter/yim24-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/winter/yim24-01.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/winter/yim24-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/winter/yim24-02.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/winter/yim24-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/winter/yim24-03.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/winter/yim24-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/winter/yim24-04.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/yim24-header-all-email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/yim24-header-all-email.png
--------------------------------------------------------------------------------
/frontend/img/year-in-music-24/yim24-header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/img/year-in-music-24/yim24-header.png
--------------------------------------------------------------------------------
/frontend/js/src/hooks/__mocks__/useFeedbackMap.tsx:
--------------------------------------------------------------------------------
1 | // Mocking this custom hook because somehow rendering alert notifications in there is making Enzyme shit itself
2 | // because Enzyme doesn't support React functional components
3 |
4 | export default function useFeedbackMap() {
5 | return { feedbackValue: 0, update: async () => {} };
6 | }
7 |
--------------------------------------------------------------------------------
/frontend/js/src/release-group/ReleaseGroup.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Navigate, useParams } from "react-router";
3 |
4 | export default function ReleaseGroup() {
5 | const { releaseGroupMBID } = useParams();
6 | return
( 7 | wrapper: ReactWrapper
| ShallowWrapper
, 8 | amount = 0 9 | ) { 10 | await act(async () => { 11 | await new Promise((resolve) => { 12 | setTimeout(resolve, amount); 13 | }); 14 | wrapper.update(); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /frontend/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /current-status 3 | Disallow: /user/ 4 | Disallow: /1/ 5 | Disallow: /api/ 6 | Disallow: /2.0/ 7 | -------------------------------------------------------------------------------- /frontend/sound/5-seconds-of-silence.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/frontend/sound/5-seconds-of-silence.mp3 -------------------------------------------------------------------------------- /listenbrainz/background/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/background/__init__.py -------------------------------------------------------------------------------- /listenbrainz/db/exceptions.py: -------------------------------------------------------------------------------- 1 | class DatabaseException(Exception): 2 | """Base exception for this package.""" 3 | pass 4 | 5 | 6 | class NoDataFoundException(DatabaseException): 7 | """Should be used when no data has been found.""" 8 | pass 9 | 10 | 11 | class BadDataException(DatabaseException): 12 | """Should be used when incorrect data is being submitted.""" 13 | pass 14 | -------------------------------------------------------------------------------- /listenbrainz/db/licenses/README.md: -------------------------------------------------------------------------------- 1 | IMPORTANT: The license file in this directory is the text file that gets included into the data dumps. 2 | 3 | The CC0 license DOES NOT APPLY to the code in this repository. 4 | -------------------------------------------------------------------------------- /listenbrainz/db/model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/db/model/__init__.py -------------------------------------------------------------------------------- /listenbrainz/db/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/db/tests/__init__.py -------------------------------------------------------------------------------- /listenbrainz/db/tests/test_dump_entry.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from listenbrainz.db.dump_entry import get_dump_entries, add_dump_entry 4 | from listenbrainz.db.testing import DatabaseTestCase 5 | 6 | 7 | class DumpEntryTestCase(DatabaseTestCase): 8 | 9 | def test_add_dump_entry(self): 10 | prev_dumps = get_dump_entries() 11 | add_dump_entry(datetime.today(), "incremental") 12 | now_dumps = get_dump_entries() 13 | self.assertEqual(len(now_dumps), len(prev_dumps) + 1) 14 | -------------------------------------------------------------------------------- /listenbrainz/domain/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/domain/__init__.py -------------------------------------------------------------------------------- /listenbrainz/domain/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/domain/tests/__init__.py -------------------------------------------------------------------------------- /listenbrainz/dumps/exceptions.py: -------------------------------------------------------------------------------- 1 | class SchemaMismatchException(Exception): 2 | pass 3 | -------------------------------------------------------------------------------- /listenbrainz/dumps/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/dumps/tests/__init__.py -------------------------------------------------------------------------------- /listenbrainz/labs_api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/labs_api/__init__.py -------------------------------------------------------------------------------- /listenbrainz/labs_api/labs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/labs_api/labs/__init__.py -------------------------------------------------------------------------------- /listenbrainz/labs_api/labs/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/labs_api/labs/api/__init__.py -------------------------------------------------------------------------------- /listenbrainz/labs_api/labs/api/apple/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from listenbrainz.labs_api.labs.api.metadata_index import BaseMetadataIndexOutput 4 | 5 | 6 | class AppleMusicIdFromMBIDOutput(BaseMetadataIndexOutput): 7 | apple_music_track_ids: Optional[list[str]] 8 | -------------------------------------------------------------------------------- /listenbrainz/labs_api/labs/api/metadata_index/__init__.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | from typing import Optional 3 | 4 | from pydantic import BaseModel 5 | 6 | 7 | class BaseMetadataIndexOutput(BaseModel): 8 | recording_mbid: Optional[uuid.UUID] 9 | artist_name: Optional[str] 10 | release_name: Optional[str] 11 | track_name: Optional[str] 12 | 13 | 14 | class AppleMusicIdFromMBIDOutput(BaseMetadataIndexOutput): 15 | apple_music_track_ids: Optional[list[str]] 16 | -------------------------------------------------------------------------------- /listenbrainz/labs_api/labs/api/soundcloud/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from listenbrainz.labs_api.labs.api.metadata_index import BaseMetadataIndexOutput 4 | 5 | 6 | class SoundCloudIdFromMBIDOutput(BaseMetadataIndexOutput): 7 | soundcloud_track_ids: Optional[list[str]] 8 | -------------------------------------------------------------------------------- /listenbrainz/labs_api/labs/api/spotify/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from listenbrainz.labs_api.labs.api.metadata_index import BaseMetadataIndexOutput 4 | 5 | 6 | class SpotifyIdFromMBIDOutput(BaseMetadataIndexOutput): 7 | spotify_track_ids: Optional[list[str]] 8 | -------------------------------------------------------------------------------- /listenbrainz/labs_api/labs/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/labs_api/labs/tests/__init__.py -------------------------------------------------------------------------------- /listenbrainz/listens_importer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/listens_importer/__init__.py -------------------------------------------------------------------------------- /listenbrainz/listens_importer/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/listens_importer/tests/__init__.py -------------------------------------------------------------------------------- /listenbrainz/listenstore/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/listenstore/tests/__init__.py -------------------------------------------------------------------------------- /listenbrainz/mbid_mapping_writer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/mbid_mapping_writer/__init__.py -------------------------------------------------------------------------------- /listenbrainz/messybrainz/exceptions.py: -------------------------------------------------------------------------------- 1 | class MessyBrainzException(Exception): 2 | """Base exception for this package.""" 3 | pass 4 | 5 | 6 | class BadDataException(MessyBrainzException): 7 | """Should be used when incorrect data is being submitted.""" 8 | pass 9 | 10 | 11 | class ErrorAddingException(MessyBrainzException): 12 | """Should be used when incorrect data is being submitted.""" 13 | pass 14 | -------------------------------------------------------------------------------- /listenbrainz/messybrainz/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/messybrainz/tests/__init__.py -------------------------------------------------------------------------------- /listenbrainz/metadata_cache/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/metadata_cache/__init__.py -------------------------------------------------------------------------------- /listenbrainz/metadata_cache/apple/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/metadata_cache/apple/__init__.py -------------------------------------------------------------------------------- /listenbrainz/metadata_cache/apple/runner.py: -------------------------------------------------------------------------------- 1 | from listenbrainz.metadata_cache.apple.handler import AppleCrawlerHandler 2 | from listenbrainz.metadata_cache.consumer import ServiceMetadataCache 3 | from listenbrainz.webserver import create_app 4 | 5 | 6 | if __name__ == "__main__": 7 | app = create_app() 8 | with app.app_context(): 9 | handler = AppleCrawlerHandler(app) 10 | smc = ServiceMetadataCache(app, handler) 11 | smc.start() 12 | -------------------------------------------------------------------------------- /listenbrainz/metadata_cache/soundcloud/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/metadata_cache/soundcloud/__init__.py -------------------------------------------------------------------------------- /listenbrainz/metadata_cache/soundcloud/models.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from pydantic import BaseModel 4 | 5 | 6 | class SoundcloudArtist(BaseModel): 7 | id: str 8 | name: str 9 | data: dict 10 | 11 | class SoundcloudTrack(BaseModel): 12 | id: str 13 | name: str 14 | release_year: Optional[int] 15 | release_month: Optional[int] 16 | release_day: Optional[int] 17 | artist: SoundcloudArtist 18 | data: dict 19 | -------------------------------------------------------------------------------- /listenbrainz/metadata_cache/soundcloud/runner.py: -------------------------------------------------------------------------------- 1 | from listenbrainz.metadata_cache.consumer import ServiceMetadataCache 2 | from listenbrainz.metadata_cache.soundcloud.handler import SoundcloudCrawlerHandler 3 | from listenbrainz.webserver import create_app 4 | 5 | 6 | if __name__ == "__main__": 7 | app = create_app() 8 | with app.app_context(): 9 | handler = SoundcloudCrawlerHandler(app) 10 | smc = ServiceMetadataCache(app, handler) 11 | smc.start() 12 | -------------------------------------------------------------------------------- /listenbrainz/metadata_cache/spotify/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/metadata_cache/spotify/__init__.py -------------------------------------------------------------------------------- /listenbrainz/metadata_cache/spotify/runner.py: -------------------------------------------------------------------------------- 1 | from listenbrainz.metadata_cache.consumer import ServiceMetadataCache 2 | from listenbrainz.metadata_cache.spotify.handler import SpotifyCrawlerHandler 3 | from listenbrainz.webserver import create_app 4 | 5 | 6 | if __name__ == "__main__": 7 | app = create_app() 8 | with app.app_context(): 9 | handler = SpotifyCrawlerHandler(app) 10 | smc = ServiceMetadataCache(app, handler) 11 | smc.start() 12 | -------------------------------------------------------------------------------- /listenbrainz/model/__init__.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | 3 | 4 | db = SQLAlchemy() 5 | 6 | 7 | # import all models here please 8 | from listenbrainz.model.external_service_oauth import ExternalService 9 | from listenbrainz.model.user import User 10 | from listenbrainz.model.listens_import import ListensImporter 11 | from listenbrainz.model.reported_users import ReportedUsers 12 | from listenbrainz.model.playlist import Playlist 13 | from listenbrainz.model.playlist_recording import PlaylistRecording 14 | -------------------------------------------------------------------------------- /listenbrainz/model/utils.py: -------------------------------------------------------------------------------- 1 | from markupsafe import Markup 2 | 3 | 4 | def generate_username_link(user_name): 5 | return Markup(f"""{user_name}""") 6 | -------------------------------------------------------------------------------- /listenbrainz/server.py: -------------------------------------------------------------------------------- 1 | from listenbrainz.webserver import create_web_app 2 | 3 | application = create_web_app() 4 | -------------------------------------------------------------------------------- /listenbrainz/spark/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/spark/__init__.py -------------------------------------------------------------------------------- /listenbrainz/spark/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/spark/tests/__init__.py -------------------------------------------------------------------------------- /listenbrainz/spark/tests/test_query_list.py: -------------------------------------------------------------------------------- 1 | import os 2 | import unittest 3 | import json 4 | 5 | JSON_FILE_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../request_queries.json') 6 | 7 | 8 | class QueryJSONTestCase(unittest.TestCase): 9 | 10 | def test_valid_request_queries_json(self): 11 | with open(JSON_FILE_PATH) as f: 12 | query_list = json.load(f) 13 | self.assertIsNotNone(query_list) 14 | -------------------------------------------------------------------------------- /listenbrainz/testdata/artist_name_list.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "single", 3 | "payload": [ 4 | { 5 | "listened_at": 1486449409, 6 | "track_metadata": { 7 | "artist_name": [ 8 | "Kanye West", 9 | "Frank Ocean" 10 | ], 11 | "track_name": "Frank's Track", 12 | "release_name": "The Life of Pablo" 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /listenbrainz/testdata/empty_artist_name.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "single", 3 | "payload": [ 4 | { 5 | "listened_at": 1486449409, 6 | "track_metadata": { 7 | "artist_name": "", 8 | "track_name": "Duuuuuuude!", 9 | "release_name": "The Life of Pablo", 10 | "additional_info": { 11 | "music_service": "spotify.com", 12 | "recording_msid": "2cfad207-3f55-4aec-8120-86cf66e34d59" 13 | } 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /listenbrainz/testdata/empty_track_name.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "single", 3 | "payload": [ 4 | { 5 | "listened_at": 1486449409, 6 | "track_metadata": { 7 | "artist_name": "Kanye West", 8 | "track_name": "", 9 | "release_name": "The Life of Pablo", 10 | "additional_info": { 11 | "music_service": "spotify.com", 12 | "recording_msid": "2cfad207-3f55-4aec-8120-86cf66e34d59" 13 | } 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /listenbrainz/testdata/invalid_artist_mbid.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": [ 3 | { 4 | "listened_at": 1486709515, 5 | "track_metadata": { 6 | "track_name": "Every Step Every Way", 7 | "artist_name": "Majid Jordan", 8 | "additional_info": { 9 | "artist_mbids": ["this mbid is not valid"] 10 | }, 11 | "release_name": "Majid Jordan" 12 | } 13 | } 14 | ], 15 | "listen_type": "import" 16 | } 17 | -------------------------------------------------------------------------------- /listenbrainz/testdata/invalid_duration.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": [ 3 | { 4 | "listened_at": 1486709515, 5 | "track_metadata": { 6 | "track_name": "Every Step Every Way", 7 | "artist_name": "Majid Jordan", 8 | "additional_info": { 9 | "duration": null 10 | }, 11 | "release_name": "Majid Jordan" 12 | } 13 | } 14 | ], 15 | "listen_type": "import" 16 | } 17 | -------------------------------------------------------------------------------- /listenbrainz/testdata/invalid_listen_missing_track_metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "single", 3 | "payload": [ 4 | { 5 | "listened_at": 1486449409, 6 | "foobar": { 7 | "artist_name": "Kanye West", 8 | "track_name": "Fade", 9 | "release_name": "The Life of Pablo", 10 | "artist_mbid": "5441c29d-3602-4898-b1a1-b77fa23b8e50" 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /listenbrainz/testdata/invalid_listen_nan_in_json.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "single", 3 | "payload": [ 4 | { 5 | "listened_at": NaN, 6 | "track_metadata": { 7 | "artist_name": "Kanye West", 8 | "track_name": "Fade", 9 | "release_name": "The Life of Pablo", 10 | "artist_mbid": "5441c29d-3602-4898-b1a1-b77fa23b8e50" 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /listenbrainz/testdata/invalid_listen_null_listened_at.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "single", 3 | "payload": [ 4 | { 5 | "listened_at": null, 6 | "track_metadata": { 7 | "artist_name": "Kanye West", 8 | "track_name": "Fade", 9 | "release_name": "The Life of Pablo", 10 | "artist_mbid": "5441c29d-3602-4898-b1a1-b77fa23b8e50" 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /listenbrainz/testdata/invalid_listen_null_track_metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "single", 3 | "payload": [ 4 | { 5 | "listened_at": 1486449409, 6 | "track_metadata": null 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /listenbrainz/testdata/invalid_recording_mbid.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": [ 3 | { 4 | "listened_at": 1486709515, 5 | "track_metadata": { 6 | "track_name": "Every Step Every Way", 7 | "artist_name": "Majid Jordan", 8 | "additional_info": { 9 | "recording_mbid": "this mbid is not valid" 10 | }, 11 | "release_name": "Majid Jordan" 12 | } 13 | } 14 | ], 15 | "listen_type": "import" 16 | } 17 | -------------------------------------------------------------------------------- /listenbrainz/testdata/invalid_release_mbid.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": [ 3 | { 4 | "listened_at": 1486709515, 5 | "track_metadata": { 6 | "track_name": "Every Step Every Way", 7 | "artist_name": "Majid Jordan", 8 | "additional_info": { 9 | "release_mbid": "this mbid is not valid" 10 | }, 11 | "release_name": "Majid Jordan" 12 | } 13 | } 14 | ], 15 | "listen_type": "import" 16 | } 17 | -------------------------------------------------------------------------------- /listenbrainz/testdata/invalid_release_name.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "single", 3 | "payload": [ 4 | { 5 | "listened_at": 1486449409, 6 | "track_metadata": { 7 | "artist_name": "Kanye West", 8 | "track_name": "The Life of Pablo", 9 | "release_name": 20, 10 | "additional_info": { 11 | "listening_from": "spotify", 12 | "recording_msid": "2cfad207-3f55-4aec-8120-86cf66e34d59" 13 | } 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /listenbrainz/testdata/listen_having_unicode_null.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "single", 3 | "payload": [ 4 | { 5 | "listened_at": 1486449409, 6 | "track_metadata": { 7 | "artist_name": "Kanye West", 8 | "track_name": "\u0000Fade", 9 | "release_name": "The Life of Pablo", 10 | "artist_mbid": "e1564e98-978b-4947-8698-f6fd6f8b0181\u0000\ufeff9ad10546-b081-4cc8-a487-3d2eece82d9e\u0000\ufeff5245e5cd-4408-4d9e-a037-c71a53edce83" 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /listenbrainz/testdata/mb_lookup_metadata_example.json: -------------------------------------------------------------------------------- 1 | { 2 | "artist_credit_name": "Rick Astley", 3 | "artist_mbids": [ 4 | "db92a151-1ac2-438b-bc43-b82e149ddd50" 5 | ], 6 | "recording_mbid": "8f3471b5-7e6a-48da-86a9-c1c07a0f47ae", 7 | "recording_name": "Never Gonna Give You Up", 8 | "release_mbid": "18550a84-4ede-451c-a91a-dd9b3af9f71d", 9 | "release_name": "Red Hot" 10 | } 11 | -------------------------------------------------------------------------------- /listenbrainz/testdata/multi_duration.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": [ 3 | { 4 | "listened_at": 1486709515, 5 | "track_metadata": { 6 | "track_name": "Every Step Every Way", 7 | "artist_name": "Majid Jordan", 8 | "additional_info": { 9 | "duration": 222, 10 | "duration_ms": 200000 11 | }, 12 | "release_name": "Majid Jordan" 13 | } 14 | } 15 | ], 16 | "listen_type": "import" 17 | } -------------------------------------------------------------------------------- /listenbrainz/testdata/playing_now_ts.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "playing_now", 3 | "payload": [ 4 | { 5 | "listened_at": 1486449409, 6 | "track_metadata": { 7 | "artist_name": "Kanye West", 8 | "release_name": "The Life of Pablo", 9 | "track_name": "Fade" 10 | } 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /listenbrainz/testdata/playing_now_with_duration.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "playing_now", 3 | "payload": [ 4 | { 5 | "track_metadata": { 6 | "artist_name": "Kanye West", 7 | "release_name": "The Life of Pablo", 8 | "track_name": "Fade", 9 | "additional_info": { 10 | "duration": 1 11 | } 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /listenbrainz/testdata/playing_now_with_duration_ms.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "playing_now", 3 | "payload": [ 4 | { 5 | "track_metadata": { 6 | "artist_name": "Kanye West", 7 | "release_name": "The Life of Pablo", 8 | "track_name": "Fade", 9 | "additional_info": { 10 | "duration_ms": 1001 11 | } 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /listenbrainz/testdata/playing_now_with_ts.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "playing_now", 3 | "payload": [ 4 | { 5 | "listened_at": 1486449409, 6 | "track_metadata": { 7 | "artist_name": "Kanye West", 8 | "release_name": "The Life of Pablo", 9 | "track_name": "Fade" 10 | } 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /listenbrainz/testdata/same_timestamp_diff_track_valid_single.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "single", 3 | "payload": [ 4 | { 5 | "listened_at": 1486449409, 6 | "track_metadata": { 7 | "artist_name": "Radiohead", 8 | "track_name": "I Promise" 9 | } 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /listenbrainz/testdata/same_timestamp_diff_track_valid_single_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "single", 3 | "payload": [ 4 | { 5 | "listened_at": 1486449409, 6 | "track_metadata": { 7 | "artist_name": "Chance The Rapper", 8 | "track_name": "All Night" 9 | } 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /listenbrainz/testdata/same_timestamp_diff_track_valid_single_3.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "single", 3 | "payload": [ 4 | { 5 | "listened_at": 1486449409, 6 | "track_metadata": { 7 | "artist_name": "Frank Ocean", 8 | "track_name": "Nikes" 9 | } 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /listenbrainz/testdata/timescale_listenstore_test_listens_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "single", 3 | "payload": [ 4 | { 5 | "track_metadata": { 6 | "track_name": "Immigrant Song 0", 7 | "additional_info": { 8 | "recording_mbid": "2cfad207-3f55-4aec-8120-86cf66e34d59" 9 | }, 10 | "artist_name": "Led Zeppelin" 11 | }, 12 | "listened_at": "1400000500", 13 | "recording_msid": "4269ddbc-9241-46da-935d-4fa9e0f7f371" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /listenbrainz/testdata/timestamp_before_lfm_founding.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "single", 3 | "payload": [ 4 | { 5 | "listened_at": 100, 6 | "track_metadata": { 7 | "artist_name": "Kanye West", 8 | "track_name": "Fade", 9 | "release_name": "The Life of Pablo" 10 | } 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /listenbrainz/testdata/timestamp_in_ns.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "single", 3 | "payload": [ 4 | { 5 | "listened_at": 1486449409000000000, 6 | "track_metadata": { 7 | "artist_name": "Kanye West", 8 | "track_name": "Fade", 9 | "release_name": "The Life of Pablo" 10 | } 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /listenbrainz/testdata/too_long_tag.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": [ 3 | { 4 | "track_metadata": { 5 | "additional_info": { 6 | "tags": [ 7 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 8 | ] 9 | }, 10 | "artist_name": "Kanye West", 11 | "release_name": "The Life of Pablo", 12 | "track_name": "Fade" 13 | } 14 | } 15 | ], 16 | "listen_type": "playing_now" 17 | } 18 | -------------------------------------------------------------------------------- /listenbrainz/testdata/user_daily_activity_db.json: -------------------------------------------------------------------------------- 1 | { 2 | "from_ts": 0, 3 | "to_ts": 10, 4 | "data": [ 5 | { 6 | "day": "Friday", 7 | "hour": 1, 8 | "listen_count": 5 9 | }, 10 | { 11 | "day": "Friday", 12 | "hour": 2, 13 | "listen_count": 5 14 | }, 15 | { 16 | "day": "Thursday", 17 | "hour": 2, 18 | "listen_count": 1 19 | } 20 | ], 21 | "stats_range": "all_time" 22 | } 23 | -------------------------------------------------------------------------------- /listenbrainz/testdata/user_listening_activity_db.json: -------------------------------------------------------------------------------- 1 | { 2 | "from_ts": 0, 3 | "to_ts": 10, 4 | "data": [ 5 | { 6 | "listen_count": 230, 7 | "time_range": "2020", 8 | "from_ts": 5, 9 | "to_ts": 10 10 | }, 11 | { 12 | "listen_count": 460, 13 | "time_range": "2019", 14 | "from_ts": 0, 15 | "to_ts": 5 16 | } 17 | ], 18 | "stats_range": "all_time" 19 | } 20 | -------------------------------------------------------------------------------- /listenbrainz/testdata/user_top_artists_db.json: -------------------------------------------------------------------------------- 1 | { 2 | "from_ts": 0, 3 | "to_ts": 10, 4 | "data": [ 5 | { 6 | "artist_mbid": "ed6c388e-ea65-431f-8be5-4959239d8c65", 7 | "listen_count": 230, 8 | "artist_name": "Kanye West" 9 | }, 10 | { 11 | "artist_mbid": "80ca06c7-07fb-4b3a-a315-ad584cc8eeb0", 12 | "listen_count": 229, 13 | "artist_name": "Frank Ocean" 14 | } 15 | ], 16 | "count": 2, 17 | "stats_range": "all_time" 18 | } 19 | -------------------------------------------------------------------------------- /listenbrainz/testdata/valid_duration.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": [ 3 | { 4 | "listened_at": 1486709515, 5 | "track_metadata": { 6 | "track_name": "Every Step Every Way", 7 | "artist_name": "Majid Jordan", 8 | "additional_info": { 9 | "duration_ms": 1222.6 10 | }, 11 | "release_name": "Majid Jordan" 12 | } 13 | } 14 | ], 15 | "listen_type": "import" 16 | } -------------------------------------------------------------------------------- /listenbrainz/testdata/valid_playing_now.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen_type": "playing_now", 3 | "payload": [ 4 | { 5 | "track_metadata": { 6 | "artist_name": "Kanye West", 7 | "release_name": "The Life of Pablo", 8 | "track_name": "Fade" 9 | } 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /listenbrainz/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/tests/__init__.py -------------------------------------------------------------------------------- /listenbrainz/timescale_writer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/timescale_writer/__init__.py -------------------------------------------------------------------------------- /listenbrainz/troi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/troi/__init__.py -------------------------------------------------------------------------------- /listenbrainz/troi/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metabrainz/listenbrainz-server/f9c919822105d14a82cc2f2abff7fa235752eae7/listenbrainz/troi/tests/__init__.py -------------------------------------------------------------------------------- /listenbrainz/webserver/admin/views.py: -------------------------------------------------------------------------------- 1 | from flask_admin import expose 2 | from listenbrainz.webserver.admin import AdminIndexView 3 | 4 | 5 | class HomeView(AdminIndexView): 6 | 7 | @expose('/') 8 | def index(self): 9 | return self.render('admin/home.html') 10 | -------------------------------------------------------------------------------- /listenbrainz/webserver/converters.py: -------------------------------------------------------------------------------- 1 | from werkzeug.routing import PathConverter, ValidationError 2 | 3 | 4 | class NotApiPathConverter(PathConverter): 5 | 6 | def to_python(self, path): 7 | if path.startswith('1/'): 8 | raise ValidationError() 9 | return path 10 | -------------------------------------------------------------------------------- /listenbrainz/webserver/templates/admin/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'admin/master.html' %} 2 | {% block body %} 3 |
2 | {{ event.user_name }} reviewed 3 | {{ event.metadata.entity_name }} 4 | on CritiqueBrainz. 5 |
6 |Rating: {{ event.metadata.rating }}/5
7 |Review: {{ event.metadata.text }}
-------------------------------------------------------------------------------- /listenbrainz/webserver/templates/atom/follow_event.html: -------------------------------------------------------------------------------- 1 |2 | {{ event.metadata.user_name_0 }} 3 | started following 4 | {{ event.metadata.user_name_1 }}. 5 |
-------------------------------------------------------------------------------- /listenbrainz/webserver/templates/atom/fresh_releases.html: -------------------------------------------------------------------------------- 1 |2 | {% if artist_mbid -%} 3 | {{ artist_name }} 4 | {%- else -%} 5 | {{ artist_name }} 6 | {%- endif %} 7 | released 8 | {% if release_mbid -%} 9 | {{ release_name }}. 10 | {%- else -%} 11 | {{ release_name }}. 12 | {%- endif %} 13 |
14 |15 | Explore more 16 |
-------------------------------------------------------------------------------- /listenbrainz/webserver/templates/atom/listens.html: -------------------------------------------------------------------------------- 1 |2 | {{ user_name }} 3 | listened to 4 | {% if recording_mbid -%} 5 | {{ track_name }} 6 | {%- else -%} 7 | {{ track_name }} 8 | {%- endif %} 9 | by 10 | {% if artist_mbid -%} 11 | {{ artist_name }} 12 | {%- else -%} 13 | {{ artist_name }}. 14 | {%- endif %} 15 |
-------------------------------------------------------------------------------- /listenbrainz/webserver/templates/atom/notification_event.html: -------------------------------------------------------------------------------- 1 |{{ event.metadata.message }}
-------------------------------------------------------------------------------- /listenbrainz/webserver/templates/atom/recording.html: -------------------------------------------------------------------------------- 1 |2 | {% if recording_mbid -%} 3 | {{ recording_title }} 4 | {%- else -%} 5 | {{ recording_title }} 6 | {%- endif %} 7 | by 8 | {% if artist_mbid -%} 9 | {{ artist_credit }} 10 | {%- else -%} 11 | {{ artist_credit }}. 12 | {%- endif %} 13 |
-------------------------------------------------------------------------------- /listenbrainz/webserver/templates/atom/top_artists.html: -------------------------------------------------------------------------------- 1 |2 |
{% block error_description %}{{ error.description }}{% endblock %}
8 |{% block error_info %}{{ error }}{% endblock %}