malojaserver 3.2.2__tar.gz → 3.2.3__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {malojaserver-3.2.2 → malojaserver-3.2.3}/PKG-INFO +10 -46
- {malojaserver-3.2.2 → malojaserver-3.2.3}/README.md +4 -42
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/__main__.py +1 -1
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/__pkginfo__.py +1 -1
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/apis/_base.py +26 -19
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/apis/_exceptions.py +1 -1
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/apis/audioscrobbler.py +35 -7
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/apis/audioscrobbler_legacy.py +5 -5
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/apis/listenbrainz.py +7 -5
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/apis/native_v1.py +43 -26
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/cleanup.py +9 -7
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_kpopgirlgroups.tsv +2 -2
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/database/__init__.py +55 -23
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/database/associated.py +10 -6
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/database/exceptions.py +28 -3
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/database/sqldb.py +216 -168
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/dev/profiler.py +3 -4
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/images.py +6 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/malojauri.py +2 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/pkg_global/conf.py +29 -28
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/proccontrol/tasks/export.py +2 -1
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/proccontrol/tasks/import_scrobbles.py +57 -15
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/server.py +4 -5
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/setup.py +13 -7
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/abstracts/base.jinja +1 -1
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/admin_albumless.jinja +2 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/admin_overview.jinja +3 -3
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/admin_setup.jinja +1 -1
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/album_showcase.jinja +1 -1
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/snippets/entityrow.jinja +2 -2
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/snippets/links.jinja +3 -1
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/css/maloja.css +8 -2
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/css/startpage.css +2 -2
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/js/manualscrobble.js +1 -1
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/js/notifications.js +16 -8
- {malojaserver-3.2.2 → malojaserver-3.2.3}/pyproject.toml +9 -7
- {malojaserver-3.2.2 → malojaserver-3.2.3}/LICENSE +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/__init__.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/apis/__init__.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/apis/_apikeys.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/cache/.maloja_cache_sentinel +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/cache/images/dummy +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/.maloja_config_sentinel +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/custom_css/customcss.info +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/.gitignore +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_artistsingroups.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_classical.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_cpop.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_dach.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_firefly-soundtrack.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_jeremysoule.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_jpop.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_kpop.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_lotr-soundtrack.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_masseffect.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_memes.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_monstercat.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_redcliff.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_specialsymbols.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_threelions.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/predefined.info +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/tdemin_kpop.tsv +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/config/rules/rules.info +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/logs/.maloja_logs_sentinel +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/logs/dbfix/dummy +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/state/.maloja_state_sentinel +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/state/auth/dummy +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/state/backups/dummy +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/state/images/albums/dummy +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/state/images/artists/dummy +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/state/images/images.info +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/state/images/tracks/dummy +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/data_files/state/scrobbles/dummy +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/database/dbcache.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/database/jinjaview.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/dev/__init__.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/dev/apidebug.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/dev/generate.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/jinjaenv/context.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/jinjaenv/filters.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/malojatime.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/pkg_global/monkey.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/proccontrol/__init__.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/proccontrol/tasks/__init__.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/proccontrol/tasks/backup.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/proccontrol/tasks/parse_albums.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/thirdparty/__init__.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/thirdparty/audiodb.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/thirdparty/deezer.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/thirdparty/lastfm.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/thirdparty/maloja.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/thirdparty/musicbrainz.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/thirdparty/spotify.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/upgrade.py +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/about.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/abstracts/admin.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/admin_apikeys.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/admin_import.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/admin_issues.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/admin_manual.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/admin_settings.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/album.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/artist.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/charts_albums.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/charts_artists.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/charts_tracks.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/error.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/LICENSE-material +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/LICENSE-octicons +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/add_album.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/add_album_confirm.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/add_artist.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/add_artist_confirm.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/association_cancel.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/association_mark.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/association_unmark.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/cert_album.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/cert_track.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/delete.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/disassociate.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/edit.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/merge.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/merge_cancel.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/merge_mark.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/merge_unmark.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/nodata.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/remove_album.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/remove_artist.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/reparse.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/icons/settings.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/awards_album.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/awards_artist.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/awards_track.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/charts_albums.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/charts_albums_tiles.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/charts_artists.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/charts_artists_tiles.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/charts_tracks.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/charts_tracks_tiles.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/info_album.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/info_artist.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/info_track.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/list_tracks.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/performance.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/pulse.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/scrobbles.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/top_albums.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/top_artists.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/partials/top_tracks.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/performance.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/pulse.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/scrobbles.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/snippets/filterdescription.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/snippets/pagination.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/snippets/timeselection.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/start.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/startpage_modules/charts_albums.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/startpage_modules/charts_artists.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/startpage_modules/charts_tracks.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/startpage_modules/featured.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/startpage_modules/lastscrobbles.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/startpage_modules/pulse.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/top_albums.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/top_artists.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/top_tracks.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/track.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/jinja/wait.jinja +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/css/grisons.css +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/css/grisonsfont.css +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/css/themes/constantinople.css +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/css/themes/kda.css +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/css/themes/maloja.css +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/ico/favicon.ico +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/ico/favicon_old.ico +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/js/datechange.js +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/js/edit.js +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/js/lazyload17-8-2.min.js +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/js/neopolitan.js +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/js/search.js +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/js/statselect.js +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/js/upload.js +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/png/chartpos_bronze.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/png/chartpos_gold.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/png/chartpos_normal.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/png/chartpos_silver.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/png/favicon.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/png/favicon_large.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/png/favicon_old.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/png/star.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/png/star_alt.png +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/svg/LICENSE +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/svg/placeholder_album.svg +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/svg/placeholder_artist.svg +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/svg/placeholder_track.svg +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/ttf/Ubuntu-Bold.ttf +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/ttf/Ubuntu-BoldItalic.ttf +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/ttf/Ubuntu-Italic.ttf +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/ttf/Ubuntu-Light.ttf +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/ttf/Ubuntu-LightItalic.ttf +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/ttf/Ubuntu-Medium.ttf +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/ttf/Ubuntu-MediumItalic.ttf +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/ttf/Ubuntu-Regular.ttf +0 -0
- {malojaserver-3.2.2 → malojaserver-3.2.3}/maloja/web/static/txt/robots.txt +0 -0
@@ -1,17 +1,17 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.3
|
2
2
|
Name: malojaserver
|
3
|
-
Version: 3.2.
|
3
|
+
Version: 3.2.3
|
4
4
|
Summary: Self-hosted music scrobble database
|
5
5
|
Keywords: scrobbling,music,selfhosted,database,charts,statistics
|
6
6
|
Author-email: Johannes Krattenmacher <maloja@dev.krateng.ch>
|
7
|
-
Requires-Python: >=3.
|
7
|
+
Requires-Python: >=3.11
|
8
8
|
Description-Content-Type: text/markdown
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
10
10
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
11
11
|
Classifier: Operating System :: OS Independent
|
12
12
|
Requires-Dist: bottle>=0.12.16
|
13
13
|
Requires-Dist: waitress>=2.1.0
|
14
|
-
Requires-Dist: doreah>=
|
14
|
+
Requires-Dist: doreah>=2.0.1, <3
|
15
15
|
Requires-Dist: nimrodel>=0.8.0
|
16
16
|
Requires-Dist: setproctitle>=1.1.10
|
17
17
|
Requires-Dist: jinja2>=3.0.0
|
@@ -21,6 +21,8 @@ Requires-Dist: sqlalchemy>=2.0
|
|
21
21
|
Requires-Dist: python-datauri>=1.1.0
|
22
22
|
Requires-Dist: requests>=2.27.1
|
23
23
|
Requires-Dist: setuptools>68.0.0
|
24
|
+
Requires-Dist: toml>=0.10.2
|
25
|
+
Requires-Dist: PyYAML>=6.0.1
|
24
26
|
Requires-Dist: pyvips>=2.1 ; extra == "full"
|
25
27
|
Project-URL: documentation, https://github.com/krateng/maloja
|
26
28
|
Project-URL: homepage, https://github.com/krateng/maloja
|
@@ -69,15 +71,8 @@ You can check [my own Maloja page](https://maloja.krateng.ch) as an example inst
|
|
69
71
|
|
70
72
|
## How to install
|
71
73
|
|
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
|
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.
|
81
76
|
|
82
77
|
Pull the [latest image](https://hub.docker.com/r/krateng/maloja) or check out the repository and use the included Containerfile.
|
83
78
|
|
@@ -96,11 +91,7 @@ An example of a minimum run configuration to access maloja via `localhost:42010`
|
|
96
91
|
docker run -p 42010:42010 -v $PWD/malojadata:/mljdata -e MALOJA_DATA_DIRECTORY=/mljdata krateng/maloja
|
97
92
|
```
|
98
93
|
|
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)
|
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)
|
104
95
|
|
105
96
|
To get the UID and GID for the current user run these commands from a terminal:
|
106
97
|
|
@@ -113,33 +104,6 @@ The modified run command with these variables would look like:
|
|
113
104
|
docker run -e PUID=1000 -e PGID=1001 -p 42010:42010 -v $PWD/malojadata:/mljdata -e MALOJA_DATA_DIRECTORY=/mljdata krateng/maloja
|
114
105
|
```
|
115
106
|
|
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
107
|
|
144
108
|
### Extras
|
145
109
|
|
@@ -160,7 +124,7 @@ When not running in a container, you can run the application with `maloja run`.
|
|
160
124
|
|
161
125
|
If you would like to import your previous scrobbles, use the command `maloja import *filename*`. This works on:
|
162
126
|
|
163
|
-
* a Last.fm export generated by [
|
127
|
+
* a Last.fm export generated by [ghan64's website](https://lastfm.ghan.nl/export/)
|
164
128
|
* an official [Spotify data export file](https://www.spotify.com/us/account/privacy/)
|
165
129
|
* an official [ListenBrainz export file](https://listenbrainz.org/profile/export/)
|
166
130
|
* the export of another Maloja instance
|
@@ -40,15 +40,8 @@ You can check [my own Maloja page](https://maloja.krateng.ch) as an example inst
|
|
40
40
|
|
41
41
|
## How to install
|
42
42
|
|
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
|
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.
|
52
45
|
|
53
46
|
Pull the [latest image](https://hub.docker.com/r/krateng/maloja) or check out the repository and use the included Containerfile.
|
54
47
|
|
@@ -67,11 +60,7 @@ An example of a minimum run configuration to access maloja via `localhost:42010`
|
|
67
60
|
docker run -p 42010:42010 -v $PWD/malojadata:/mljdata -e MALOJA_DATA_DIRECTORY=/mljdata krateng/maloja
|
68
61
|
```
|
69
62
|
|
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)
|
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)
|
75
64
|
|
76
65
|
To get the UID and GID for the current user run these commands from a terminal:
|
77
66
|
|
@@ -84,33 +73,6 @@ The modified run command with these variables would look like:
|
|
84
73
|
docker run -e PUID=1000 -e PGID=1001 -p 42010:42010 -v $PWD/malojadata:/mljdata -e MALOJA_DATA_DIRECTORY=/mljdata krateng/maloja
|
85
74
|
```
|
86
75
|
|
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
76
|
|
115
77
|
### Extras
|
116
78
|
|
@@ -131,7 +93,7 @@ When not running in a container, you can run the application with `maloja run`.
|
|
131
93
|
|
132
94
|
If you would like to import your previous scrobbles, use the command `maloja import *filename*`. This works on:
|
133
95
|
|
134
|
-
* a Last.fm export generated by [
|
96
|
+
* a Last.fm export generated by [ghan64's website](https://lastfm.ghan.nl/export/)
|
135
97
|
* an official [Spotify data export file](https://www.spotify.com/us/account/privacy/)
|
136
98
|
* an official [ListenBrainz export file](https://listenbrainz.org/profile/export/)
|
137
99
|
* the export of another Maloja instance
|
@@ -135,7 +135,7 @@ def debug():
|
|
135
135
|
def print_info():
|
136
136
|
print_header_info()
|
137
137
|
print(col['lightblue']("Configuration Directory:"),conf.dir_settings['config'])
|
138
|
-
print(col['lightblue']("
|
138
|
+
print(col['lightblue']("State Directory: "),conf.dir_settings['state'])
|
139
139
|
print(col['lightblue']("Log Directory: "),conf.dir_settings['logs'])
|
140
140
|
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
141
|
print(col['lightblue']("Timezone: "),f"UTC{conf.malojaconfig['timezone']:+d}")
|
@@ -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
|
6
7
|
|
7
8
|
from ..pkg_global.conf import malojaconfig
|
8
9
|
|
@@ -21,11 +22,12 @@ 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
|
+
Exception: (500, {"code": 500, "error": "Unspecified server error."})
|
29
31
|
}
|
30
32
|
|
31
33
|
def get_method(self,pathnodes,keys):
|
@@ -7,7 +7,6 @@ from bottle import response, static_file, FormsDict
|
|
7
7
|
from inspect import signature
|
8
8
|
|
9
9
|
from doreah.logging import log
|
10
|
-
from doreah.auth import authenticated_function
|
11
10
|
|
12
11
|
# nimrodel API
|
13
12
|
from nimrodel import EAPI as API
|
@@ -15,7 +14,7 @@ from nimrodel import Multi
|
|
15
14
|
|
16
15
|
|
17
16
|
from .. import database
|
18
|
-
from ..pkg_global.conf import malojaconfig, data_dir
|
17
|
+
from ..pkg_global.conf import malojaconfig, data_dir, auth
|
19
18
|
|
20
19
|
|
21
20
|
|
@@ -82,6 +81,24 @@ errors = {
|
|
82
81
|
'desc':"This entity does not exist in the database."
|
83
82
|
}
|
84
83
|
}),
|
84
|
+
database.exceptions.DuplicateTimestamp: lambda e: (409,{
|
85
|
+
"status":"error",
|
86
|
+
"error":{
|
87
|
+
'type':'duplicate_timestamp',
|
88
|
+
'value':e.rejected_scrobble,
|
89
|
+
'desc':"A scrobble is already registered with this timestamp."
|
90
|
+
}
|
91
|
+
}),
|
92
|
+
database.exceptions.DuplicateScrobble: lambda e: (200,{
|
93
|
+
"status": "success",
|
94
|
+
"desc": "The scrobble is present in the database.",
|
95
|
+
"track": {},
|
96
|
+
"warnings": [{
|
97
|
+
'type': 'scrobble_exists',
|
98
|
+
'value': None,
|
99
|
+
'desc': 'This scrobble exists in the database (same timestamp and track). The submitted scrobble was not added.'
|
100
|
+
}]
|
101
|
+
}),
|
85
102
|
images.MalformedB64: lambda e: (400,{
|
86
103
|
"status":"failure",
|
87
104
|
"error":{
|
@@ -474,7 +491,7 @@ def get_top_artists_external(k_filter, k_limit, k_delimit, k_amount):
|
|
474
491
|
:rtype: Dictionary"""
|
475
492
|
|
476
493
|
ckeys = {**k_limit, **k_delimit}
|
477
|
-
results = database.get_top_artists(**ckeys)
|
494
|
+
results = database.get_top_artists(**ckeys,compatibility=True)
|
478
495
|
|
479
496
|
return {
|
480
497
|
"status":"ok",
|
@@ -493,7 +510,7 @@ def get_top_tracks_external(k_filter, k_limit, k_delimit, k_amount):
|
|
493
510
|
:rtype: Dictionary"""
|
494
511
|
|
495
512
|
ckeys = {**k_limit, **k_delimit}
|
496
|
-
results = database.get_top_tracks(**ckeys)
|
513
|
+
results = database.get_top_tracks(**ckeys,compatibility=True)
|
497
514
|
# IMPLEMENT THIS FOR TOP TRACKS OF ARTIST/ALBUM AS WELL?
|
498
515
|
|
499
516
|
return {
|
@@ -513,7 +530,7 @@ def get_top_albums_external(k_filter, k_limit, k_delimit, k_amount):
|
|
513
530
|
:rtype: Dictionary"""
|
514
531
|
|
515
532
|
ckeys = {**k_limit, **k_delimit}
|
516
|
-
results = database.get_top_albums(**ckeys)
|
533
|
+
results = database.get_top_albums(**ckeys,compatibility=True)
|
517
534
|
# IMPLEMENT THIS FOR TOP ALBUMS OF ARTIST AS WELL?
|
518
535
|
|
519
536
|
return {
|
@@ -567,7 +584,7 @@ def album_info_external(k_filter, k_limit, k_delimit, k_amount):
|
|
567
584
|
|
568
585
|
|
569
586
|
@api.post("newscrobble")
|
570
|
-
@authenticated_function(alternate=api_key_correct,api=True,pass_auth_result_as='auth_result')
|
587
|
+
@auth.authenticated_function(alternate=api_key_correct,api=True,pass_auth_result_as='auth_result')
|
571
588
|
@catch_exceptions
|
572
589
|
def post_scrobble(
|
573
590
|
artist:Multi=None,
|
@@ -647,7 +664,7 @@ def post_scrobble(
|
|
647
664
|
|
648
665
|
|
649
666
|
@api.post("addpicture")
|
650
|
-
@authenticated_function(alternate=api_key_correct,api=True)
|
667
|
+
@auth.authenticated_function(alternate=api_key_correct,api=True)
|
651
668
|
@catch_exceptions
|
652
669
|
@convert_kwargs
|
653
670
|
def add_picture(k_filter, k_limit, k_delimit, k_amount, k_special):
|
@@ -670,7 +687,7 @@ def add_picture(k_filter, k_limit, k_delimit, k_amount, k_special):
|
|
670
687
|
|
671
688
|
|
672
689
|
@api.post("importrules")
|
673
|
-
@authenticated_function(api=True)
|
690
|
+
@auth.authenticated_function(api=True)
|
674
691
|
@catch_exceptions
|
675
692
|
def import_rulemodule(**keys):
|
676
693
|
"""Internal Use Only"""
|
@@ -689,7 +706,7 @@ def import_rulemodule(**keys):
|
|
689
706
|
|
690
707
|
|
691
708
|
@api.post("rebuild")
|
692
|
-
@authenticated_function(api=True)
|
709
|
+
@auth.authenticated_function(api=True)
|
693
710
|
@catch_exceptions
|
694
711
|
def rebuild(**keys):
|
695
712
|
"""Internal Use Only"""
|
@@ -765,7 +782,7 @@ def search(**keys):
|
|
765
782
|
|
766
783
|
|
767
784
|
@api.post("newrule")
|
768
|
-
@authenticated_function(api=True)
|
785
|
+
@auth.authenticated_function(api=True)
|
769
786
|
@catch_exceptions
|
770
787
|
def newrule(**keys):
|
771
788
|
"""Internal Use Only"""
|
@@ -776,21 +793,21 @@ def newrule(**keys):
|
|
776
793
|
|
777
794
|
|
778
795
|
@api.post("settings")
|
779
|
-
@authenticated_function(api=True)
|
796
|
+
@auth.authenticated_function(api=True)
|
780
797
|
@catch_exceptions
|
781
798
|
def set_settings(**keys):
|
782
799
|
"""Internal Use Only"""
|
783
800
|
malojaconfig.update(keys)
|
784
801
|
|
785
802
|
@api.post("apikeys")
|
786
|
-
@authenticated_function(api=True)
|
803
|
+
@auth.authenticated_function(api=True)
|
787
804
|
@catch_exceptions
|
788
805
|
def set_apikeys(**keys):
|
789
806
|
"""Internal Use Only"""
|
790
807
|
apikeystore.update(keys)
|
791
808
|
|
792
809
|
@api.post("import")
|
793
|
-
@authenticated_function(api=True)
|
810
|
+
@auth.authenticated_function(api=True)
|
794
811
|
@catch_exceptions
|
795
812
|
def import_scrobbles(identifier):
|
796
813
|
"""Internal Use Only"""
|
@@ -798,7 +815,7 @@ def import_scrobbles(identifier):
|
|
798
815
|
import_scrobbles(identifier)
|
799
816
|
|
800
817
|
@api.get("backup")
|
801
|
-
@authenticated_function(api=True)
|
818
|
+
@auth.authenticated_function(api=True)
|
802
819
|
@catch_exceptions
|
803
820
|
def get_backup(**keys):
|
804
821
|
"""Internal Use Only"""
|
@@ -811,7 +828,7 @@ def get_backup(**keys):
|
|
811
828
|
return static_file(os.path.basename(archivefile),root=tmpfolder)
|
812
829
|
|
813
830
|
@api.get("export")
|
814
|
-
@authenticated_function(api=True)
|
831
|
+
@auth.authenticated_function(api=True)
|
815
832
|
@catch_exceptions
|
816
833
|
def get_export(**keys):
|
817
834
|
"""Internal Use Only"""
|
@@ -825,7 +842,7 @@ def get_export(**keys):
|
|
825
842
|
|
826
843
|
|
827
844
|
@api.post("delete_scrobble")
|
828
|
-
@authenticated_function(api=True)
|
845
|
+
@auth.authenticated_function(api=True)
|
829
846
|
@catch_exceptions
|
830
847
|
def delete_scrobble(timestamp):
|
831
848
|
"""Internal Use Only"""
|
@@ -837,7 +854,7 @@ def delete_scrobble(timestamp):
|
|
837
854
|
|
838
855
|
|
839
856
|
@api.post("edit_artist")
|
840
|
-
@authenticated_function(api=True)
|
857
|
+
@auth.authenticated_function(api=True)
|
841
858
|
@catch_exceptions
|
842
859
|
def edit_artist(id,name):
|
843
860
|
"""Internal Use Only"""
|
@@ -847,7 +864,7 @@ def edit_artist(id,name):
|
|
847
864
|
}
|
848
865
|
|
849
866
|
@api.post("edit_track")
|
850
|
-
@authenticated_function(api=True)
|
867
|
+
@auth.authenticated_function(api=True)
|
851
868
|
@catch_exceptions
|
852
869
|
def edit_track(id,title):
|
853
870
|
"""Internal Use Only"""
|
@@ -857,7 +874,7 @@ def edit_track(id,title):
|
|
857
874
|
}
|
858
875
|
|
859
876
|
@api.post("edit_album")
|
860
|
-
@authenticated_function(api=True)
|
877
|
+
@auth.authenticated_function(api=True)
|
861
878
|
@catch_exceptions
|
862
879
|
def edit_album(id,albumtitle):
|
863
880
|
"""Internal Use Only"""
|
@@ -868,7 +885,7 @@ def edit_album(id,albumtitle):
|
|
868
885
|
|
869
886
|
|
870
887
|
@api.post("merge_tracks")
|
871
|
-
@authenticated_function(api=True)
|
888
|
+
@auth.authenticated_function(api=True)
|
872
889
|
@catch_exceptions
|
873
890
|
def merge_tracks(target_id,source_ids):
|
874
891
|
"""Internal Use Only"""
|
@@ -879,7 +896,7 @@ def merge_tracks(target_id,source_ids):
|
|
879
896
|
}
|
880
897
|
|
881
898
|
@api.post("merge_artists")
|
882
|
-
@authenticated_function(api=True)
|
899
|
+
@auth.authenticated_function(api=True)
|
883
900
|
@catch_exceptions
|
884
901
|
def merge_artists(target_id,source_ids):
|
885
902
|
"""Internal Use Only"""
|
@@ -890,7 +907,7 @@ def merge_artists(target_id,source_ids):
|
|
890
907
|
}
|
891
908
|
|
892
909
|
@api.post("merge_albums")
|
893
|
-
@authenticated_function(api=True)
|
910
|
+
@auth.authenticated_function(api=True)
|
894
911
|
@catch_exceptions
|
895
912
|
def merge_artists(target_id,source_ids):
|
896
913
|
"""Internal Use Only"""
|
@@ -901,7 +918,7 @@ def merge_artists(target_id,source_ids):
|
|
901
918
|
}
|
902
919
|
|
903
920
|
@api.post("associate_albums_to_artist")
|
904
|
-
@authenticated_function(api=True)
|
921
|
+
@auth.authenticated_function(api=True)
|
905
922
|
@catch_exceptions
|
906
923
|
def associate_albums_to_artist(target_id,source_ids,remove=False):
|
907
924
|
result = database.associate_albums_to_artist(target_id,source_ids,remove=remove)
|
@@ -913,7 +930,7 @@ def associate_albums_to_artist(target_id,source_ids,remove=False):
|
|
913
930
|
}
|
914
931
|
|
915
932
|
@api.post("associate_tracks_to_artist")
|
916
|
-
@authenticated_function(api=True)
|
933
|
+
@auth.authenticated_function(api=True)
|
917
934
|
@catch_exceptions
|
918
935
|
def associate_tracks_to_artist(target_id,source_ids,remove=False):
|
919
936
|
result = database.associate_tracks_to_artist(target_id,source_ids,remove=remove)
|
@@ -925,7 +942,7 @@ def associate_tracks_to_artist(target_id,source_ids,remove=False):
|
|
925
942
|
}
|
926
943
|
|
927
944
|
@api.post("associate_tracks_to_album")
|
928
|
-
@authenticated_function(api=True)
|
945
|
+
@auth.authenticated_function(api=True)
|
929
946
|
@catch_exceptions
|
930
947
|
def associate_tracks_to_album(target_id,source_ids):
|
931
948
|
result = database.associate_tracks_to_album(target_id,source_ids)
|
@@ -937,7 +954,7 @@ def associate_tracks_to_album(target_id,source_ids):
|
|
937
954
|
|
938
955
|
|
939
956
|
@api.post("reparse_scrobble")
|
940
|
-
@authenticated_function(api=True)
|
957
|
+
@auth.authenticated_function(api=True)
|
941
958
|
@catch_exceptions
|
942
959
|
def reparse_scrobble(timestamp):
|
943
960
|
"""Internal Use Only"""
|
@@ -15,13 +15,15 @@ class CleanerAgent:
|
|
15
15
|
def updateRules(self):
|
16
16
|
|
17
17
|
rawrules = []
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
try:
|
19
|
+
for f in os.listdir(data_dir["rules"]()):
|
20
|
+
if f.split('.')[-1].lower() != 'tsv': continue
|
21
|
+
filepath = data_dir["rules"](f)
|
22
|
+
with open(filepath,'r') as filed:
|
23
|
+
reader = csv.reader(filed,delimiter="\t")
|
24
|
+
rawrules += [[col for col in entry if col] for entry in reader if len(entry)>0 and not entry[0].startswith('#')]
|
25
|
+
except FileNotFoundError:
|
26
|
+
pass
|
25
27
|
|
26
28
|
self.rules_belongtogether = [r[1] for r in rawrules if r[0]=="belongtogether"]
|
27
29
|
self.rules_notanartist = [r[1] for r in rawrules if r[0]=="notanartist"]
|