malojaserver 3.2.1__tar.gz → 3.2.3__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (204) hide show
  1. {malojaserver-3.2.1 → malojaserver-3.2.3}/PKG-INFO +10 -46
  2. {malojaserver-3.2.1 → malojaserver-3.2.3}/README.md +4 -42
  3. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/__main__.py +1 -1
  4. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/__pkginfo__.py +1 -1
  5. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/apis/_base.py +26 -19
  6. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/apis/_exceptions.py +1 -1
  7. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/apis/audioscrobbler.py +35 -7
  8. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/apis/audioscrobbler_legacy.py +5 -5
  9. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/apis/listenbrainz.py +7 -5
  10. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/apis/native_v1.py +43 -26
  11. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/cleanup.py +9 -7
  12. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_kpopgirlgroups.tsv +4 -2
  13. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/database/__init__.py +55 -23
  14. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/database/associated.py +10 -6
  15. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/database/exceptions.py +28 -3
  16. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/database/sqldb.py +216 -168
  17. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/dev/profiler.py +3 -4
  18. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/images.py +6 -0
  19. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/malojauri.py +2 -0
  20. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/pkg_global/conf.py +97 -72
  21. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/proccontrol/tasks/export.py +2 -1
  22. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/proccontrol/tasks/import_scrobbles.py +57 -15
  23. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/server.py +4 -5
  24. malojaserver-3.2.3/maloja/setup.py +94 -0
  25. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/thirdparty/lastfm.py +18 -17
  26. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/abstracts/base.jinja +1 -1
  27. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/admin_albumless.jinja +2 -0
  28. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/admin_overview.jinja +3 -3
  29. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/admin_setup.jinja +1 -1
  30. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/error.jinja +2 -2
  31. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/album_showcase.jinja +1 -1
  32. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/awards_album.jinja +1 -1
  33. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/awards_artist.jinja +2 -2
  34. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/charts_albums_tiles.jinja +4 -0
  35. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/charts_artists_tiles.jinja +5 -1
  36. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/charts_tracks_tiles.jinja +4 -0
  37. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/snippets/entityrow.jinja +2 -2
  38. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/snippets/links.jinja +3 -1
  39. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/css/maloja.css +14 -2
  40. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/css/startpage.css +2 -2
  41. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/js/manualscrobble.js +1 -1
  42. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/js/notifications.js +16 -8
  43. {malojaserver-3.2.1 → malojaserver-3.2.3}/pyproject.toml +9 -7
  44. malojaserver-3.2.1/maloja/setup.py +0 -82
  45. {malojaserver-3.2.1 → malojaserver-3.2.3}/LICENSE +0 -0
  46. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/__init__.py +0 -0
  47. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/apis/__init__.py +0 -0
  48. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/apis/_apikeys.py +0 -0
  49. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/cache/.maloja_cache_sentinel +0 -0
  50. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/cache/images/dummy +0 -0
  51. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/.maloja_config_sentinel +0 -0
  52. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/custom_css/customcss.info +0 -0
  53. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/.gitignore +0 -0
  54. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_artistsingroups.tsv +0 -0
  55. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_classical.tsv +0 -0
  56. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_cpop.tsv +0 -0
  57. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_dach.tsv +0 -0
  58. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_firefly-soundtrack.tsv +0 -0
  59. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_jeremysoule.tsv +0 -0
  60. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_jpop.tsv +0 -0
  61. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_kpop.tsv +0 -0
  62. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_lotr-soundtrack.tsv +0 -0
  63. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_masseffect.tsv +0 -0
  64. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_memes.tsv +0 -0
  65. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_monstercat.tsv +0 -0
  66. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_redcliff.tsv +0 -0
  67. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_specialsymbols.tsv +0 -0
  68. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/krateng_threelions.tsv +0 -0
  69. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/predefined.info +0 -0
  70. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/predefined/tdemin_kpop.tsv +0 -0
  71. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/config/rules/rules.info +0 -0
  72. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/logs/.maloja_logs_sentinel +0 -0
  73. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/logs/dbfix/dummy +0 -0
  74. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/state/.maloja_state_sentinel +0 -0
  75. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/state/auth/dummy +0 -0
  76. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/state/backups/dummy +0 -0
  77. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/state/images/albums/dummy +0 -0
  78. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/state/images/artists/dummy +0 -0
  79. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/state/images/images.info +0 -0
  80. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/state/images/tracks/dummy +0 -0
  81. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/data_files/state/scrobbles/dummy +0 -0
  82. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/database/dbcache.py +0 -0
  83. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/database/jinjaview.py +0 -0
  84. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/dev/__init__.py +0 -0
  85. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/dev/apidebug.py +0 -0
  86. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/dev/generate.py +0 -0
  87. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/jinjaenv/context.py +0 -0
  88. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/jinjaenv/filters.py +0 -0
  89. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/malojatime.py +0 -0
  90. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/pkg_global/monkey.py +0 -0
  91. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/proccontrol/__init__.py +0 -0
  92. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/proccontrol/tasks/__init__.py +0 -0
  93. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/proccontrol/tasks/backup.py +0 -0
  94. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/proccontrol/tasks/parse_albums.py +0 -0
  95. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/thirdparty/__init__.py +0 -0
  96. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/thirdparty/audiodb.py +0 -0
  97. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/thirdparty/deezer.py +0 -0
  98. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/thirdparty/maloja.py +0 -0
  99. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/thirdparty/musicbrainz.py +0 -0
  100. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/thirdparty/spotify.py +0 -0
  101. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/upgrade.py +0 -0
  102. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/about.jinja +0 -0
  103. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/abstracts/admin.jinja +0 -0
  104. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/admin_apikeys.jinja +0 -0
  105. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/admin_import.jinja +0 -0
  106. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/admin_issues.jinja +0 -0
  107. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/admin_manual.jinja +0 -0
  108. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/admin_settings.jinja +0 -0
  109. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/album.jinja +0 -0
  110. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/artist.jinja +0 -0
  111. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/charts_albums.jinja +0 -0
  112. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/charts_artists.jinja +0 -0
  113. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/charts_tracks.jinja +0 -0
  114. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/LICENSE-material +0 -0
  115. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/LICENSE-octicons +0 -0
  116. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/add_album.jinja +0 -0
  117. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/add_album_confirm.jinja +0 -0
  118. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/add_artist.jinja +0 -0
  119. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/add_artist_confirm.jinja +0 -0
  120. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/association_cancel.jinja +0 -0
  121. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/association_mark.jinja +0 -0
  122. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/association_unmark.jinja +0 -0
  123. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/cert_album.jinja +0 -0
  124. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/cert_track.jinja +0 -0
  125. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/delete.jinja +0 -0
  126. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/disassociate.jinja +0 -0
  127. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/edit.jinja +0 -0
  128. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/merge.jinja +0 -0
  129. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/merge_cancel.jinja +0 -0
  130. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/merge_mark.jinja +0 -0
  131. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/merge_unmark.jinja +0 -0
  132. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/nodata.jinja +0 -0
  133. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/remove_album.jinja +0 -0
  134. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/remove_artist.jinja +0 -0
  135. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/reparse.jinja +0 -0
  136. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/icons/settings.jinja +0 -0
  137. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/awards_track.jinja +0 -0
  138. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/charts_albums.jinja +0 -0
  139. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/charts_artists.jinja +0 -0
  140. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/charts_tracks.jinja +0 -0
  141. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/info_album.jinja +0 -0
  142. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/info_artist.jinja +0 -0
  143. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/info_track.jinja +0 -0
  144. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/list_tracks.jinja +0 -0
  145. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/performance.jinja +0 -0
  146. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/pulse.jinja +0 -0
  147. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/scrobbles.jinja +0 -0
  148. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/top_albums.jinja +0 -0
  149. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/top_artists.jinja +0 -0
  150. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/partials/top_tracks.jinja +0 -0
  151. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/performance.jinja +0 -0
  152. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/pulse.jinja +0 -0
  153. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/scrobbles.jinja +0 -0
  154. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/snippets/filterdescription.jinja +0 -0
  155. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/snippets/pagination.jinja +0 -0
  156. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/snippets/timeselection.jinja +0 -0
  157. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/start.jinja +0 -0
  158. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/startpage_modules/charts_albums.jinja +0 -0
  159. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/startpage_modules/charts_artists.jinja +0 -0
  160. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/startpage_modules/charts_tracks.jinja +0 -0
  161. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/startpage_modules/featured.jinja +0 -0
  162. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/startpage_modules/lastscrobbles.jinja +0 -0
  163. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/startpage_modules/pulse.jinja +0 -0
  164. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/top_albums.jinja +0 -0
  165. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/top_artists.jinja +0 -0
  166. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/top_tracks.jinja +0 -0
  167. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/track.jinja +0 -0
  168. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/jinja/wait.jinja +0 -0
  169. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/css/grisons.css +0 -0
  170. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/css/grisonsfont.css +0 -0
  171. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/css/themes/constantinople.css +0 -0
  172. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/css/themes/kda.css +0 -0
  173. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/css/themes/maloja.css +0 -0
  174. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/ico/favicon.ico +0 -0
  175. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/ico/favicon_old.ico +0 -0
  176. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/js/datechange.js +0 -0
  177. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/js/edit.js +0 -0
  178. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/js/lazyload17-8-2.min.js +0 -0
  179. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/js/neopolitan.js +0 -0
  180. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/js/search.js +0 -0
  181. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/js/statselect.js +0 -0
  182. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/js/upload.js +0 -0
  183. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/png/chartpos_bronze.png +0 -0
  184. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/png/chartpos_gold.png +0 -0
  185. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/png/chartpos_normal.png +0 -0
  186. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/png/chartpos_silver.png +0 -0
  187. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/png/favicon.png +0 -0
  188. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/png/favicon_large.png +0 -0
  189. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/png/favicon_old.png +0 -0
  190. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/png/star.png +0 -0
  191. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/png/star_alt.png +0 -0
  192. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/svg/LICENSE +0 -0
  193. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/svg/placeholder_album.svg +0 -0
  194. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/svg/placeholder_artist.svg +0 -0
  195. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/svg/placeholder_track.svg +0 -0
  196. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/ttf/Ubuntu-Bold.ttf +0 -0
  197. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/ttf/Ubuntu-BoldItalic.ttf +0 -0
  198. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/ttf/Ubuntu-Italic.ttf +0 -0
  199. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/ttf/Ubuntu-Light.ttf +0 -0
  200. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/ttf/Ubuntu-LightItalic.ttf +0 -0
  201. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/ttf/Ubuntu-Medium.ttf +0 -0
  202. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/ttf/Ubuntu-MediumItalic.ttf +0 -0
  203. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/ttf/Ubuntu-Regular.ttf +0 -0
  204. {malojaserver-3.2.1 → malojaserver-3.2.3}/maloja/web/static/txt/robots.txt +0 -0
@@ -1,17 +1,17 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: malojaserver
3
- Version: 3.2.1
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.10
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>=1.9.4, <2
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
- ### Requirements
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
- #### Linux Host
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 [benfoxall's website](https://benjaminbenben.com/lastfm-to-csv/) ([GitHub page](https://github.com/benfoxall/lastfm-to-csv))
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
- ### Requirements
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
- #### Linux Host
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 [benfoxall's website](https://benjaminbenben.com/lastfm-to-csv/) ([GitHub page](https://github.com/benfoxall/lastfm-to-csv))
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']("Data Directory: "),conf.dir_settings['state'])
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}")
@@ -4,7 +4,7 @@
4
4
  # you know what f*ck it
5
5
  # this is hardcoded for now because of that damn project / package name discrepancy
6
6
  # i'll fix it one day
7
- VERSION = "3.2.1"
7
+ VERSION = "3.2.3"
8
8
  HOMEPAGE = "https://github.com/krateng/maloja"
9
9
 
10
10
 
@@ -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
- exceptiontype = sys.exc_info()[0]
67
- if exceptiontype in self.errors:
68
- response.status,result = self.errors[exceptiontype]
69
- log(f"Error with {self.__apiname__} API: {exceptiontype} (Request: {path})")
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
- response.status,result = 500,{"status":"Unknown error","code":500}
72
- log(f"Unhandled Exception with {self.__apiname__} API: {exceptiontype} (Request: {path})")
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 Exception:
86
- log("Could not find a handler for method " + str(methodname) + " in API " + self.__apiname__,module="debug")
87
- log("Keys: " + str(keys),module="debug")
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
- try:
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)
@@ -3,4 +3,4 @@ class InvalidAuthException(Exception): pass
3
3
  class InvalidMethodException(Exception): pass
4
4
  class InvalidSessionKey(Exception): pass
5
5
  class MalformedJSONException(Exception): pass
6
- class ScrobblingException(Exception): pass
6
+
@@ -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
- ScrobblingException:(500,{"error":8,"message":"Operation failed"})
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("&", "&amp;")
34
+ str_xml = str_xml.replace("<", "&lt;")
35
+ str_xml = str_xml.replace("<", "&lt;")
36
+ str_xml = str_xml.replace("\"", "&quot;")
37
+ str_xml = str_xml.replace("'", "&apos;")
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
- return 200,{"session":{"key":sessionkey}}
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
- return 200,{"session":{"key":sessionkey}}
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
- ScrobblingException:(500,"FAILED\n")
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
- ScrobblingException:(500,{"code":500,"error":"Unspecified server error."})
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
- for f in os.listdir(data_dir["rules"]()):
19
- if f.split('.')[-1].lower() != 'tsv': continue
20
- filepath = data_dir["rules"](f)
21
- with open(filepath,'r') as filed:
22
- reader = csv.reader(filed,delimiter="\t")
23
- rawrules += [[col for col in entry if col] for entry in reader if len(entry)>0 and not entry[0].startswith('#')]
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"]