malojaserver 3.2.2__tar.gz → 3.2.4__tar.gz

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