malojaserver 3.2.2__tar.gz → 3.2.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- malojaserver-3.2.2/README.md → malojaserver-3.2.4/PKG-INFO +38 -56
- malojaserver-3.2.2/PKG-INFO → malojaserver-3.2.4/README.md +6 -86
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/__main__.py +1 -94
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/__pkginfo__.py +1 -1
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/apis/_base.py +26 -19
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/apis/_exceptions.py +1 -1
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/apis/audioscrobbler.py +35 -7
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/apis/audioscrobbler_legacy.py +5 -5
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/apis/listenbrainz.py +8 -5
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/apis/native_v1.py +43 -26
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/cleanup.py +9 -7
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/krateng_kpopgirlgroups.tsv +2 -2
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/database/__init__.py +68 -23
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/database/associated.py +10 -6
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/database/exceptions.py +28 -3
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/database/sqldb.py +216 -168
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/dev/profiler.py +3 -4
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/images.py +6 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/malojauri.py +2 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/pkg_global/conf.py +30 -28
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/proccontrol/tasks/export.py +2 -1
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/proccontrol/tasks/import_scrobbles.py +110 -47
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/server.py +11 -10
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/setup.py +29 -17
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/abstracts/base.jinja +1 -1
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/admin_albumless.jinja +2 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/admin_issues.jinja +2 -2
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/admin_overview.jinja +3 -3
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/admin_setup.jinja +3 -3
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/album_showcase.jinja +1 -1
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/snippets/entityrow.jinja +2 -2
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/snippets/links.jinja +3 -1
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/css/maloja.css +8 -2
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/css/startpage.css +2 -2
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/js/manualscrobble.js +2 -2
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/js/notifications.js +16 -8
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/js/search.js +18 -12
- malojaserver-3.2.4/maloja/web/static/js/upload.js +3 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/pyproject.toml +22 -20
- malojaserver-3.2.2/maloja/web/static/js/upload.js +0 -3
- {malojaserver-3.2.2 → malojaserver-3.2.4}/LICENSE +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/__init__.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/apis/__init__.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/apis/_apikeys.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/cache/.maloja_cache_sentinel +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/cache/images/dummy +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/.maloja_config_sentinel +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/custom_css/customcss.info +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/.gitignore +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/krateng_artistsingroups.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/krateng_classical.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/krateng_cpop.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/krateng_dach.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/krateng_firefly-soundtrack.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/krateng_jeremysoule.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/krateng_jpop.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/krateng_kpop.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/krateng_lotr-soundtrack.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/krateng_masseffect.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/krateng_memes.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/krateng_monstercat.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/krateng_redcliff.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/krateng_specialsymbols.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/krateng_threelions.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/predefined.info +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/predefined/tdemin_kpop.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/config/rules/rules.info +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/logs/.maloja_logs_sentinel +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/logs/dbfix/dummy +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/state/.maloja_state_sentinel +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/state/auth/dummy +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/state/backups/dummy +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/state/images/albums/dummy +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/state/images/artists/dummy +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/state/images/images.info +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/data_files/state/images/tracks/dummy +0 -0
- {malojaserver-3.2.2/maloja/data_files/state/scrobbles → malojaserver-3.2.4/maloja/data_files/state/import}/dummy +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/database/dbcache.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/database/jinjaview.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/dev/__init__.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/dev/apidebug.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/dev/generate.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/jinjaenv/context.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/jinjaenv/filters.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/malojatime.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/pkg_global/monkey.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/proccontrol/__init__.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/proccontrol/tasks/__init__.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/proccontrol/tasks/backup.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/proccontrol/tasks/parse_albums.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/thirdparty/__init__.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/thirdparty/audiodb.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/thirdparty/deezer.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/thirdparty/lastfm.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/thirdparty/maloja.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/thirdparty/musicbrainz.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/thirdparty/spotify.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/upgrade.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/about.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/abstracts/admin.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/admin_apikeys.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/admin_import.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/admin_manual.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/admin_settings.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/album.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/artist.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/charts_albums.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/charts_artists.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/charts_tracks.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/error.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/LICENSE-material +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/LICENSE-octicons +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/add_album.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/add_album_confirm.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/add_artist.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/add_artist_confirm.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/association_cancel.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/association_mark.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/association_unmark.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/cert_album.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/cert_track.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/delete.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/disassociate.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/edit.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/merge.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/merge_cancel.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/merge_mark.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/merge_unmark.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/nodata.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/remove_album.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/remove_artist.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/reparse.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/icons/settings.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/awards_album.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/awards_artist.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/awards_track.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/charts_albums.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/charts_albums_tiles.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/charts_artists.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/charts_artists_tiles.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/charts_tracks.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/charts_tracks_tiles.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/info_album.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/info_artist.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/info_track.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/list_tracks.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/performance.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/pulse.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/scrobbles.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/top_albums.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/top_artists.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/partials/top_tracks.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/performance.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/pulse.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/scrobbles.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/snippets/filterdescription.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/snippets/pagination.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/snippets/timeselection.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/start.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/startpage_modules/charts_albums.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/startpage_modules/charts_artists.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/startpage_modules/charts_tracks.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/startpage_modules/featured.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/startpage_modules/lastscrobbles.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/startpage_modules/pulse.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/top_albums.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/top_artists.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/top_tracks.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/track.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/jinja/wait.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/css/grisons.css +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/css/grisonsfont.css +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/css/themes/constantinople.css +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/css/themes/kda.css +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/css/themes/maloja.css +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/ico/favicon.ico +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/ico/favicon_old.ico +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/js/datechange.js +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/js/edit.js +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/js/lazyload17-8-2.min.js +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/js/neopolitan.js +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/js/statselect.js +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/png/chartpos_bronze.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/png/chartpos_gold.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/png/chartpos_normal.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/png/chartpos_silver.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/png/favicon.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/png/favicon_large.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/png/favicon_old.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/png/star.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/png/star_alt.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/svg/LICENSE +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/svg/placeholder_album.svg +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/svg/placeholder_artist.svg +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/svg/placeholder_track.svg +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/ttf/Ubuntu-Bold.ttf +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/ttf/Ubuntu-BoldItalic.ttf +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/ttf/Ubuntu-Italic.ttf +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/ttf/Ubuntu-Light.ttf +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/ttf/Ubuntu-LightItalic.ttf +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/ttf/Ubuntu-Medium.ttf +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/ttf/Ubuntu-MediumItalic.ttf +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/ttf/Ubuntu-Regular.ttf +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.4}/maloja/web/static/txt/robots.txt +0 -0
@@ -1,3 +1,34 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: malojaserver
|
3
|
+
Version: 3.2.4
|
4
|
+
Summary: Self-hosted music scrobble database
|
5
|
+
Keywords: scrobbling,music,selfhosted,database,charts,statistics
|
6
|
+
Author-email: Johannes Krattenmacher <maloja@dev.krateng.ch>
|
7
|
+
Requires-Python: ==3.12.*
|
8
|
+
Description-Content-Type: text/markdown
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
10
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
11
|
+
Classifier: Operating System :: OS Independent
|
12
|
+
Requires-Dist: bottle==0.13.*
|
13
|
+
Requires-Dist: waitress==3.0.*
|
14
|
+
Requires-Dist: doreah==2.0.*
|
15
|
+
Requires-Dist: nimrodel==0.8.*
|
16
|
+
Requires-Dist: setproctitle==1.3.*
|
17
|
+
Requires-Dist: jinja2==3.1.*
|
18
|
+
Requires-Dist: lru-dict==1.3.*
|
19
|
+
Requires-Dist: psutil==5.9.*
|
20
|
+
Requires-Dist: sqlalchemy==2.0
|
21
|
+
Requires-Dist: python-datauri==3.0.*
|
22
|
+
Requires-Dist: python-magic==0.4.*
|
23
|
+
Requires-Dist: requests==2.32.*
|
24
|
+
Requires-Dist: toml==0.10.*
|
25
|
+
Requires-Dist: PyYAML==6.0.*
|
26
|
+
Requires-Dist: pyvips==2.2.* ; extra == "full"
|
27
|
+
Project-URL: documentation, https://github.com/krateng/maloja
|
28
|
+
Project-URL: homepage, https://github.com/krateng/maloja
|
29
|
+
Project-URL: repository, https://github.com/krateng/maloja
|
30
|
+
Provides-Extra: full
|
31
|
+
|
1
32
|
# Maloja
|
2
33
|
|
3
34
|
[](https://github.com/krateng/maloja)
|
@@ -40,15 +71,8 @@ You can check [my own Maloja page](https://maloja.krateng.ch) as an example inst
|
|
40
71
|
|
41
72
|
## How to install
|
42
73
|
|
43
|
-
|
44
|
-
|
45
|
-
Maloja should run on any x86 or ARM machine that runs Python.
|
46
|
-
|
47
|
-
It is highly recommended to use **Docker** or **Podman**.
|
48
|
-
|
49
|
-
Your CPU should have a single core passmark score of at the very least 1500. 500 MB RAM should give you a decent experience, but performance will benefit greatly from up to 2 GB.
|
50
|
-
|
51
|
-
### Docker / Podman
|
74
|
+
To avoid issues with version / dependency mismatches, Maloja should only be used in **Docker** or **Podman**, not on bare metal.
|
75
|
+
I cannot offer any help for bare metal installations (but using venv should help).
|
52
76
|
|
53
77
|
Pull the [latest image](https://hub.docker.com/r/krateng/maloja) or check out the repository and use the included Containerfile.
|
54
78
|
|
@@ -67,11 +91,7 @@ An example of a minimum run configuration to access maloja via `localhost:42010`
|
|
67
91
|
docker run -p 42010:42010 -v $PWD/malojadata:/mljdata -e MALOJA_DATA_DIRECTORY=/mljdata krateng/maloja
|
68
92
|
```
|
69
93
|
|
70
|
-
|
71
|
-
|
72
|
-
**NOTE:** If you are using [rootless containers with Podman](https://developers.redhat.com/blog/2020/09/25/rootless-containers-with-podman-the-basics#why_podman_) this DOES NOT apply to you.
|
73
|
-
|
74
|
-
If you are running Docker on a **Linux Host** you should specify `user:group` ids of the user who owns the folder on the host machine bound to `MALOJA_DATA_DIRECTORY` in order to avoid [docker file permission problems.](https://ikriv.com/blog/?p=4698) These can be specified using the [environmental variables **PUID** and **PGID**.](https://docs.linuxserver.io/general/understanding-puid-and-pgid)
|
94
|
+
If you are using [rootless containers with Podman](https://developers.redhat.com/blog/2020/09/25/rootless-containers-with-podman-the-basics#why_podman_) the following DOES NOT apply to you, but if you are running **Docker** on a **Linux Host** you should specify `user:group` ids of the user who owns the folder on the host machine bound to `MALOJA_DATA_DIRECTORY` in order to avoid [docker file permission problems.](https://ikriv.com/blog/?p=4698) These can be specified using the [environmental variables **PUID** and **PGID**.](https://docs.linuxserver.io/general/understanding-puid-and-pgid)
|
75
95
|
|
76
96
|
To get the UID and GID for the current user run these commands from a terminal:
|
77
97
|
|
@@ -84,33 +104,6 @@ The modified run command with these variables would look like:
|
|
84
104
|
docker run -e PUID=1000 -e PGID=1001 -p 42010:42010 -v $PWD/malojadata:/mljdata -e MALOJA_DATA_DIRECTORY=/mljdata krateng/maloja
|
85
105
|
```
|
86
106
|
|
87
|
-
### PyPI
|
88
|
-
|
89
|
-
You can install Maloja with
|
90
|
-
|
91
|
-
```console
|
92
|
-
pip install malojaserver
|
93
|
-
```
|
94
|
-
|
95
|
-
To make sure all dependencies are installed, you can also use one of the included scripts in the `install` folder.
|
96
|
-
|
97
|
-
### From Source
|
98
|
-
|
99
|
-
Clone this repository and enter the directory with
|
100
|
-
|
101
|
-
```console
|
102
|
-
git clone https://github.com/krateng/maloja
|
103
|
-
cd maloja
|
104
|
-
```
|
105
|
-
|
106
|
-
Then install all the requirements and build the package, e.g.:
|
107
|
-
|
108
|
-
```console
|
109
|
-
sh ./install/install_dependencies_alpine.sh
|
110
|
-
pip install -r requirements.txt
|
111
|
-
pip install .
|
112
|
-
```
|
113
|
-
|
114
107
|
|
115
108
|
### Extras
|
116
109
|
|
@@ -123,30 +116,18 @@ Then install all the requirements and build the package, e.g.:
|
|
123
116
|
|
124
117
|
### Basic control
|
125
118
|
|
126
|
-
When not running in a container, you can run the application with `maloja run`.
|
127
|
-
`maloja start` and `maloja stop`, but this might not be supported in the future.
|
119
|
+
When not running in a container, you can run the application with `maloja run`.
|
128
120
|
|
129
121
|
|
130
122
|
### Data
|
131
123
|
|
132
|
-
If you would like to import your previous scrobbles,
|
124
|
+
If you would like to import your previous scrobbles, copy them into the import folder in your data directory. This works on:
|
133
125
|
|
134
|
-
* a Last.fm export generated by [
|
126
|
+
* a Last.fm export generated by [ghan64's website](https://lastfm.ghan.nl/export/)
|
135
127
|
* an official [Spotify data export file](https://www.spotify.com/us/account/privacy/)
|
136
128
|
* an official [ListenBrainz export file](https://listenbrainz.org/profile/export/)
|
137
129
|
* the export of another Maloja instance
|
138
130
|
|
139
|
-
⚠️ Never import your data while maloja is running. When you need to do import inside docker container start it in shell mode instead and perform import before starting the container as mentioned above.
|
140
|
-
|
141
|
-
```console
|
142
|
-
docker run -it --entrypoint sh -v $PWD/malojadata:/mljdata -e MALOJA_DATA_DIRECTORY=/mljdata krateng/maloja
|
143
|
-
cd /mljdata
|
144
|
-
maloja import my_last_fm_export.csv
|
145
|
-
```
|
146
|
-
|
147
|
-
|
148
|
-
To backup your data, run `maloja backup`, optional with `--include_images`.
|
149
|
-
|
150
131
|
### Customization
|
151
132
|
|
152
133
|
* Have a look at the [available settings](settings.md) and specifiy your choices in `/etc/maloja/settings.ini`. You can also set each of these settings as an environment variable with the prefix `MALOJA_` (e.g. `MALOJA_SKIP_SETUP`).
|
@@ -170,3 +151,4 @@ If you can't automatically scrobble your music, you can always do it manually on
|
|
170
151
|
## How to extend
|
171
152
|
|
172
153
|
If you'd like to implement anything on top of Maloja, visit `/api_explorer`.
|
154
|
+
|
@@ -1,32 +1,3 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: malojaserver
|
3
|
-
Version: 3.2.2
|
4
|
-
Summary: Self-hosted music scrobble database
|
5
|
-
Keywords: scrobbling,music,selfhosted,database,charts,statistics
|
6
|
-
Author-email: Johannes Krattenmacher <maloja@dev.krateng.ch>
|
7
|
-
Requires-Python: >=3.10
|
8
|
-
Description-Content-Type: text/markdown
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
10
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
11
|
-
Classifier: Operating System :: OS Independent
|
12
|
-
Requires-Dist: bottle>=0.12.16
|
13
|
-
Requires-Dist: waitress>=2.1.0
|
14
|
-
Requires-Dist: doreah>=1.9.4, <2
|
15
|
-
Requires-Dist: nimrodel>=0.8.0
|
16
|
-
Requires-Dist: setproctitle>=1.1.10
|
17
|
-
Requires-Dist: jinja2>=3.0.0
|
18
|
-
Requires-Dist: lru-dict>=1.1.6
|
19
|
-
Requires-Dist: psutil>=5.8.0
|
20
|
-
Requires-Dist: sqlalchemy>=2.0
|
21
|
-
Requires-Dist: python-datauri>=1.1.0
|
22
|
-
Requires-Dist: requests>=2.27.1
|
23
|
-
Requires-Dist: setuptools>68.0.0
|
24
|
-
Requires-Dist: pyvips>=2.1 ; extra == "full"
|
25
|
-
Project-URL: documentation, https://github.com/krateng/maloja
|
26
|
-
Project-URL: homepage, https://github.com/krateng/maloja
|
27
|
-
Project-URL: repository, https://github.com/krateng/maloja
|
28
|
-
Provides-Extra: full
|
29
|
-
|
30
1
|
# Maloja
|
31
2
|
|
32
3
|
[](https://github.com/krateng/maloja)
|
@@ -69,15 +40,8 @@ You can check [my own Maloja page](https://maloja.krateng.ch) as an example inst
|
|
69
40
|
|
70
41
|
## How to install
|
71
42
|
|
72
|
-
|
73
|
-
|
74
|
-
Maloja should run on any x86 or ARM machine that runs Python.
|
75
|
-
|
76
|
-
It is highly recommended to use **Docker** or **Podman**.
|
77
|
-
|
78
|
-
Your CPU should have a single core passmark score of at the very least 1500. 500 MB RAM should give you a decent experience, but performance will benefit greatly from up to 2 GB.
|
79
|
-
|
80
|
-
### Docker / Podman
|
43
|
+
To avoid issues with version / dependency mismatches, Maloja should only be used in **Docker** or **Podman**, not on bare metal.
|
44
|
+
I cannot offer any help for bare metal installations (but using venv should help).
|
81
45
|
|
82
46
|
Pull the [latest image](https://hub.docker.com/r/krateng/maloja) or check out the repository and use the included Containerfile.
|
83
47
|
|
@@ -96,11 +60,7 @@ An example of a minimum run configuration to access maloja via `localhost:42010`
|
|
96
60
|
docker run -p 42010:42010 -v $PWD/malojadata:/mljdata -e MALOJA_DATA_DIRECTORY=/mljdata krateng/maloja
|
97
61
|
```
|
98
62
|
|
99
|
-
|
100
|
-
|
101
|
-
**NOTE:** If you are using [rootless containers with Podman](https://developers.redhat.com/blog/2020/09/25/rootless-containers-with-podman-the-basics#why_podman_) this DOES NOT apply to you.
|
102
|
-
|
103
|
-
If you are running Docker on a **Linux Host** you should specify `user:group` ids of the user who owns the folder on the host machine bound to `MALOJA_DATA_DIRECTORY` in order to avoid [docker file permission problems.](https://ikriv.com/blog/?p=4698) These can be specified using the [environmental variables **PUID** and **PGID**.](https://docs.linuxserver.io/general/understanding-puid-and-pgid)
|
63
|
+
If you are using [rootless containers with Podman](https://developers.redhat.com/blog/2020/09/25/rootless-containers-with-podman-the-basics#why_podman_) the following DOES NOT apply to you, but if you are running **Docker** on a **Linux Host** you should specify `user:group` ids of the user who owns the folder on the host machine bound to `MALOJA_DATA_DIRECTORY` in order to avoid [docker file permission problems.](https://ikriv.com/blog/?p=4698) These can be specified using the [environmental variables **PUID** and **PGID**.](https://docs.linuxserver.io/general/understanding-puid-and-pgid)
|
104
64
|
|
105
65
|
To get the UID and GID for the current user run these commands from a terminal:
|
106
66
|
|
@@ -113,33 +73,6 @@ The modified run command with these variables would look like:
|
|
113
73
|
docker run -e PUID=1000 -e PGID=1001 -p 42010:42010 -v $PWD/malojadata:/mljdata -e MALOJA_DATA_DIRECTORY=/mljdata krateng/maloja
|
114
74
|
```
|
115
75
|
|
116
|
-
### PyPI
|
117
|
-
|
118
|
-
You can install Maloja with
|
119
|
-
|
120
|
-
```console
|
121
|
-
pip install malojaserver
|
122
|
-
```
|
123
|
-
|
124
|
-
To make sure all dependencies are installed, you can also use one of the included scripts in the `install` folder.
|
125
|
-
|
126
|
-
### From Source
|
127
|
-
|
128
|
-
Clone this repository and enter the directory with
|
129
|
-
|
130
|
-
```console
|
131
|
-
git clone https://github.com/krateng/maloja
|
132
|
-
cd maloja
|
133
|
-
```
|
134
|
-
|
135
|
-
Then install all the requirements and build the package, e.g.:
|
136
|
-
|
137
|
-
```console
|
138
|
-
sh ./install/install_dependencies_alpine.sh
|
139
|
-
pip install -r requirements.txt
|
140
|
-
pip install .
|
141
|
-
```
|
142
|
-
|
143
76
|
|
144
77
|
### Extras
|
145
78
|
|
@@ -152,30 +85,18 @@ Then install all the requirements and build the package, e.g.:
|
|
152
85
|
|
153
86
|
### Basic control
|
154
87
|
|
155
|
-
When not running in a container, you can run the application with `maloja run`.
|
156
|
-
`maloja start` and `maloja stop`, but this might not be supported in the future.
|
88
|
+
When not running in a container, you can run the application with `maloja run`.
|
157
89
|
|
158
90
|
|
159
91
|
### Data
|
160
92
|
|
161
|
-
If you would like to import your previous scrobbles,
|
93
|
+
If you would like to import your previous scrobbles, copy them into the import folder in your data directory. This works on:
|
162
94
|
|
163
|
-
* a Last.fm export generated by [
|
95
|
+
* a Last.fm export generated by [ghan64's website](https://lastfm.ghan.nl/export/)
|
164
96
|
* an official [Spotify data export file](https://www.spotify.com/us/account/privacy/)
|
165
97
|
* an official [ListenBrainz export file](https://listenbrainz.org/profile/export/)
|
166
98
|
* the export of another Maloja instance
|
167
99
|
|
168
|
-
⚠️ Never import your data while maloja is running. When you need to do import inside docker container start it in shell mode instead and perform import before starting the container as mentioned above.
|
169
|
-
|
170
|
-
```console
|
171
|
-
docker run -it --entrypoint sh -v $PWD/malojadata:/mljdata -e MALOJA_DATA_DIRECTORY=/mljdata krateng/maloja
|
172
|
-
cd /mljdata
|
173
|
-
maloja import my_last_fm_export.csv
|
174
|
-
```
|
175
|
-
|
176
|
-
|
177
|
-
To backup your data, run `maloja backup`, optional with `--include_images`.
|
178
|
-
|
179
100
|
### Customization
|
180
101
|
|
181
102
|
* Have a look at the [available settings](settings.md) and specifiy your choices in `/etc/maloja/settings.ini`. You can also set each of these settings as an environment variable with the prefix `MALOJA_` (e.g. `MALOJA_SKIP_SETUP`).
|
@@ -199,4 +120,3 @@ If you can't automatically scrobble your music, you can always do it manually on
|
|
199
120
|
## How to extend
|
200
121
|
|
201
122
|
If you'd like to implement anything on top of Maloja, visit `/api_explorer`.
|
202
|
-
|
@@ -26,77 +26,6 @@ def print_header_info():
|
|
26
26
|
#print("#####")
|
27
27
|
print()
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
def get_instance():
|
32
|
-
try:
|
33
|
-
return int(subprocess.check_output(["pgrep","-f","maloja$"]))
|
34
|
-
except Exception:
|
35
|
-
return None
|
36
|
-
|
37
|
-
def get_instance_supervisor():
|
38
|
-
try:
|
39
|
-
return int(subprocess.check_output(["pgrep","-f","maloja_supervisor"]))
|
40
|
-
except Exception:
|
41
|
-
return None
|
42
|
-
|
43
|
-
def restart():
|
44
|
-
if stop():
|
45
|
-
start()
|
46
|
-
else:
|
47
|
-
print(col["red"]("Could not stop Maloja!"))
|
48
|
-
|
49
|
-
def start():
|
50
|
-
if get_instance_supervisor() is not None:
|
51
|
-
print("Maloja is already running.")
|
52
|
-
else:
|
53
|
-
print_header_info()
|
54
|
-
setup()
|
55
|
-
try:
|
56
|
-
#p = subprocess.Popen(["python3","-m","maloja.server"],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)
|
57
|
-
sp = subprocess.Popen(["python3","-m","maloja","supervisor"],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)
|
58
|
-
print(col["green"]("Maloja started!"))
|
59
|
-
|
60
|
-
port = conf.malojaconfig["PORT"]
|
61
|
-
|
62
|
-
print("Visit your server address (Port " + str(port) + ") to see your web interface. Visit /admin_setup to get started.")
|
63
|
-
print("If you're installing this on your local machine, these links should get you there:")
|
64
|
-
print("\t" + col["blue"]("http://localhost:" + str(port)))
|
65
|
-
print("\t" + col["blue"]("http://localhost:" + str(port) + "/admin_setup"))
|
66
|
-
return True
|
67
|
-
except Exception:
|
68
|
-
print("Error while starting Maloja.")
|
69
|
-
return False
|
70
|
-
|
71
|
-
|
72
|
-
def stop():
|
73
|
-
|
74
|
-
for attempt in [(signal.SIGTERM,2),(signal.SIGTERM,5),(signal.SIGKILL,3),(signal.SIGKILL,5)]:
|
75
|
-
|
76
|
-
pid_sv = get_instance_supervisor()
|
77
|
-
pid = get_instance()
|
78
|
-
|
79
|
-
if pid is None and pid_sv is None:
|
80
|
-
print("Maloja stopped!")
|
81
|
-
return True
|
82
|
-
|
83
|
-
if pid_sv is not None:
|
84
|
-
os.kill(pid_sv,attempt[0])
|
85
|
-
if pid is not None:
|
86
|
-
os.kill(pid,attempt[0])
|
87
|
-
|
88
|
-
time.sleep(attempt[1])
|
89
|
-
|
90
|
-
return False
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
print("Maloja stopped!")
|
98
|
-
return True
|
99
|
-
|
100
29
|
def onlysetup():
|
101
30
|
print_header_info()
|
102
31
|
setup()
|
@@ -109,24 +38,6 @@ def run_server():
|
|
109
38
|
from . import server
|
110
39
|
server.run_server()
|
111
40
|
|
112
|
-
def run_supervisor():
|
113
|
-
setproctitle("maloja_supervisor")
|
114
|
-
while True:
|
115
|
-
log("Maloja is not running, starting...",module="supervisor")
|
116
|
-
try:
|
117
|
-
process = subprocess.Popen(
|
118
|
-
["python3", "-m", "maloja","run"],
|
119
|
-
stdout=subprocess.DEVNULL,
|
120
|
-
stderr=subprocess.DEVNULL,
|
121
|
-
)
|
122
|
-
except Exception as e:
|
123
|
-
log("Error starting Maloja: " + str(e),module="supervisor")
|
124
|
-
else:
|
125
|
-
try:
|
126
|
-
process.wait()
|
127
|
-
except Exception as e:
|
128
|
-
log("Maloja crashed: " + str(e),module="supervisor")
|
129
|
-
|
130
41
|
def debug():
|
131
42
|
os.environ["MALOJA_DEV_MODE"] = 'true'
|
132
43
|
conf.malojaconfig.load_environment()
|
@@ -135,7 +46,7 @@ def debug():
|
|
135
46
|
def print_info():
|
136
47
|
print_header_info()
|
137
48
|
print(col['lightblue']("Configuration Directory:"),conf.dir_settings['config'])
|
138
|
-
print(col['lightblue']("
|
49
|
+
print(col['lightblue']("State Directory: "),conf.dir_settings['state'])
|
139
50
|
print(col['lightblue']("Log Directory: "),conf.dir_settings['logs'])
|
140
51
|
print(col['lightblue']("Network: "),f"Dual Stack, Port {conf.malojaconfig['port']}" if conf.malojaconfig['host'] == "*" else f"IPv{ip_address(conf.malojaconfig['host']).version}, Port {conf.malojaconfig['port']}")
|
141
52
|
print(col['lightblue']("Timezone: "),f"UTC{conf.malojaconfig['timezone']:+d}")
|
@@ -173,11 +84,7 @@ def main(*args,**kwargs):
|
|
173
84
|
|
174
85
|
actions = {
|
175
86
|
# server
|
176
|
-
"start":start,
|
177
|
-
"restart":restart,
|
178
|
-
"stop":stop,
|
179
87
|
"run":run_server,
|
180
|
-
"supervisor":run_supervisor,
|
181
88
|
"debug":debug,
|
182
89
|
"setup":onlysetup,
|
183
90
|
# admin scripts
|
@@ -25,9 +25,20 @@ __logmodulename__ = "apis"
|
|
25
25
|
|
26
26
|
cla = CleanerAgent()
|
27
27
|
|
28
|
+
|
29
|
+
|
30
|
+
# wrapper method: calls handle. final net to catch exceptions and map them to the handlers proper json / xml response
|
31
|
+
# handle method: finds the method for this path / query. can only raise InvalidMethodException
|
32
|
+
# scrobble: NOT the exposed scrobble method - helper for all APIs to scrobble their results with self-identification
|
33
|
+
|
34
|
+
|
28
35
|
class APIHandler:
|
36
|
+
|
37
|
+
__apiname__: str
|
38
|
+
errors: dict
|
29
39
|
# make these classes singletons
|
30
40
|
_instance = None
|
41
|
+
|
31
42
|
def __new__(cls, *args, **kwargs):
|
32
43
|
if not isinstance(cls._instance, cls):
|
33
44
|
cls._instance = object.__new__(cls, *args, **kwargs)
|
@@ -62,37 +73,33 @@ class APIHandler:
|
|
62
73
|
|
63
74
|
try:
|
64
75
|
response.status,result = self.handle(path,keys)
|
65
|
-
except Exception:
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
76
|
+
except Exception as e:
|
77
|
+
for exc_type, exc_response in self.errors.items():
|
78
|
+
if isinstance(e, exc_type):
|
79
|
+
response.status, result = exc_response
|
80
|
+
log(f"Error with {self.__apiname__} API: {e} (Request: {path})")
|
81
|
+
break
|
70
82
|
else:
|
71
|
-
|
72
|
-
|
83
|
+
# THIS SHOULD NOT HAPPEN
|
84
|
+
response.status, result = 500, {"status": "Unknown error", "code": 500}
|
85
|
+
log(f"Unhandled Exception with {self.__apiname__} API: {e} (Request: {path})")
|
73
86
|
|
74
87
|
return result
|
75
|
-
#else:
|
76
|
-
# result = {"error":"Invalid scrobble protocol"}
|
77
|
-
# response.status = 500
|
78
88
|
|
79
89
|
|
80
90
|
def handle(self,path,keys):
|
81
91
|
|
82
92
|
try:
|
83
|
-
methodname = self.get_method(path,keys)
|
93
|
+
methodname = self.get_method(path, keys)
|
84
94
|
method = self.methods[methodname]
|
85
|
-
except
|
86
|
-
log("Could not find a handler for method
|
87
|
-
log("Keys: "
|
95
|
+
except KeyError:
|
96
|
+
log(f"Could not find a handler for method {methodname} in API {self.__apiname__}", module="debug")
|
97
|
+
log(f"Keys: {keys}", module="debug")
|
88
98
|
raise InvalidMethodException()
|
89
|
-
return method(path,keys)
|
99
|
+
return method(path, keys)
|
90
100
|
|
91
101
|
|
92
102
|
def scrobble(self,rawscrobble,client=None):
|
93
103
|
|
94
104
|
# fixing etc is handled by the main scrobble function
|
95
|
-
|
96
|
-
return database.incoming_scrobble(rawscrobble,api=self.__apiname__,client=client)
|
97
|
-
except Exception:
|
98
|
-
raise ScrobblingException()
|
105
|
+
return database.incoming_scrobble(rawscrobble,api=self.__apiname__,client=client)
|
@@ -21,13 +21,22 @@ class Audioscrobbler(APIHandler):
|
|
21
21
|
"track.scrobble":self.submit_scrobble
|
22
22
|
}
|
23
23
|
self.errors = {
|
24
|
-
BadAuthException:(400,{"error":6,"message":"Requires authentication"}),
|
25
|
-
InvalidAuthException:(401,{"error":4,"message":"Invalid credentials"}),
|
26
|
-
InvalidMethodException:(200,{"error":3,"message":"Invalid method"}),
|
27
|
-
InvalidSessionKey:(403,{"error":9,"message":"Invalid session key"}),
|
28
|
-
|
24
|
+
BadAuthException: (400, {"error": 6, "message": "Requires authentication"}),
|
25
|
+
InvalidAuthException: (401, {"error": 4, "message": "Invalid credentials"}),
|
26
|
+
InvalidMethodException: (200, {"error": 3, "message": "Invalid method"}),
|
27
|
+
InvalidSessionKey: (403, {"error": 9, "message": "Invalid session key"}),
|
28
|
+
Exception: (500, {"error": 8, "message": "Operation failed"})
|
29
29
|
}
|
30
30
|
|
31
|
+
# xml string escaping: https://stackoverflow.com/a/28703510
|
32
|
+
def xml_escape(self, str_xml: str):
|
33
|
+
str_xml = str_xml.replace("&", "&")
|
34
|
+
str_xml = str_xml.replace("<", "<")
|
35
|
+
str_xml = str_xml.replace("<", "<")
|
36
|
+
str_xml = str_xml.replace("\"", """)
|
37
|
+
str_xml = str_xml.replace("'", "'")
|
38
|
+
return str_xml
|
39
|
+
|
31
40
|
def get_method(self,pathnodes,keys):
|
32
41
|
return keys.get("method")
|
33
42
|
|
@@ -45,12 +54,22 @@ class Audioscrobbler(APIHandler):
|
|
45
54
|
token = keys.get("authToken")
|
46
55
|
user = keys.get("username")
|
47
56
|
password = keys.get("password")
|
57
|
+
format = keys.get("format") or "xml" # Audioscrobbler 2.0 uses XML by default
|
48
58
|
# either username and password
|
49
59
|
if user is not None and password is not None:
|
50
60
|
client = apikeystore.check_and_identify_key(password)
|
51
61
|
if client:
|
52
62
|
sessionkey = self.generate_key(client)
|
53
|
-
|
63
|
+
if format == "json":
|
64
|
+
return 200,{"session":{"key":sessionkey}}
|
65
|
+
else:
|
66
|
+
return 200,"""<lfm status="ok">
|
67
|
+
<session>
|
68
|
+
<name>%s</name>
|
69
|
+
<key>%s</key>
|
70
|
+
<subscriber>0</subscriber>
|
71
|
+
</session>
|
72
|
+
</lfm>""" % (self.xml_escape(user), self.xml_escape(sessionkey))
|
54
73
|
else:
|
55
74
|
raise InvalidAuthException()
|
56
75
|
# or username and token (deprecated by lastfm)
|
@@ -59,7 +78,16 @@ class Audioscrobbler(APIHandler):
|
|
59
78
|
key = apikeystore[client]
|
60
79
|
if md5(user + md5(key)) == token:
|
61
80
|
sessionkey = self.generate_key(client)
|
62
|
-
|
81
|
+
if format == "json":
|
82
|
+
return 200,{"session":{"key":sessionkey}}
|
83
|
+
else:
|
84
|
+
return 200,"""<lfm status="ok">
|
85
|
+
<session>
|
86
|
+
<name>%s</name>
|
87
|
+
<key>%s</key>
|
88
|
+
<subscriber>0</subscriber>
|
89
|
+
</session>
|
90
|
+
</lfm>""" % (self.xml_escape(user), self.xml_escape(sessionkey))
|
63
91
|
raise InvalidAuthException()
|
64
92
|
else:
|
65
93
|
raise BadAuthException()
|
@@ -23,11 +23,11 @@ class AudioscrobblerLegacy(APIHandler):
|
|
23
23
|
"scrobble":self.submit_scrobble
|
24
24
|
}
|
25
25
|
self.errors = {
|
26
|
-
BadAuthException:(403,"BADAUTH\n"),
|
27
|
-
InvalidAuthException:(403,"BADAUTH\n"),
|
28
|
-
InvalidMethodException:(400,"FAILED\n"),
|
29
|
-
InvalidSessionKey:(403,"BADSESSION\n"),
|
30
|
-
|
26
|
+
BadAuthException: (403, "BADAUTH\n"),
|
27
|
+
InvalidAuthException: (403, "BADAUTH\n"),
|
28
|
+
InvalidMethodException: (400, "FAILED\n"),
|
29
|
+
InvalidSessionKey: (403, "BADSESSION\n"),
|
30
|
+
Exception: (500, "FAILED\n")
|
31
31
|
}
|
32
32
|
|
33
33
|
def get_method(self,pathnodes,keys):
|
@@ -3,6 +3,7 @@ from ._exceptions import *
|
|
3
3
|
from .. import database
|
4
4
|
import datetime
|
5
5
|
from ._apikeys import apikeystore
|
6
|
+
from ..database.exceptions import DuplicateScrobble, DuplicateTimestamp
|
6
7
|
|
7
8
|
from ..pkg_global.conf import malojaconfig
|
8
9
|
|
@@ -21,11 +22,13 @@ class Listenbrainz(APIHandler):
|
|
21
22
|
"validate-token":self.validate_token
|
22
23
|
}
|
23
24
|
self.errors = {
|
24
|
-
BadAuthException:(401,{"code":401,"error":"You need to provide an Authorization header."}),
|
25
|
-
InvalidAuthException:(401,{"code":401,"error":"Incorrect Authorization"}),
|
26
|
-
InvalidMethodException:(200,{"code":200,"error":"Invalid Method"}),
|
27
|
-
MalformedJSONException:(400,{"code":400,"error":"Invalid JSON document submitted."}),
|
28
|
-
|
25
|
+
BadAuthException: (401, {"code": 401, "error": "You need to provide an Authorization header."}),
|
26
|
+
InvalidAuthException: (401, {"code": 401, "error": "Incorrect Authorization"}),
|
27
|
+
InvalidMethodException: (200, {"code": 200, "error": "Invalid Method"}),
|
28
|
+
MalformedJSONException: (400, {"code": 400, "error": "Invalid JSON document submitted."}),
|
29
|
+
DuplicateScrobble: (200, {"status": "ok"}),
|
30
|
+
DuplicateTimestamp: (409, {"error": "Scrobble with the same timestamp already exists."}),
|
31
|
+
Exception: (500, {"code": 500, "error": "Unspecified server error."})
|
29
32
|
}
|
30
33
|
|
31
34
|
def get_method(self,pathnodes,keys):
|