mirror of
https://github.com/krateng/maloja.git
synced 2023-08-10 21:12:55 +03:00
Merge branch 'master' of github.com:krateng/maloja
This commit is contained in:
commit
663c6a0372
@ -11,7 +11,7 @@ RUN apk add --no-cache --virtual .build-deps \
|
|||||||
linux-headers \
|
linux-headers \
|
||||||
&& \
|
&& \
|
||||||
pip3 install psutil && \
|
pip3 install psutil && \
|
||||||
pip3 install malojaserver && \
|
pip3 install --no-cache-dir malojaserver && \
|
||||||
apk del .build-deps
|
apk del .build-deps
|
||||||
|
|
||||||
EXPOSE 42010
|
EXPOSE 42010
|
||||||
|
@ -151,6 +151,8 @@ Auth Token | Any of your API keys
|
|||||||
Known working scrobblers:
|
Known working scrobblers:
|
||||||
* [Pano Scrobbler](https://github.com/kawaiiDango/pScrobbler) for Android
|
* [Pano Scrobbler](https://github.com/kawaiiDango/pScrobbler) for Android
|
||||||
* [Web Scrobbler](https://github.com/web-scrobbler/web-scrobbler) for desktop browsers (requires you to supply the full endpoint (`yoururl.tld/apis/listenbrainz/1/submit-listens`))
|
* [Web Scrobbler](https://github.com/web-scrobbler/web-scrobbler) for desktop browsers (requires you to supply the full endpoint (`yoururl.tld/apis/listenbrainz/1/submit-listens`))
|
||||||
|
* [Simple Scrobbler](https://simple-last-fm-scrobbler.github.io) for Android
|
||||||
|
* [Airsonic Advanced](https://github.com/airsonic-advanced/airsonic-advanced) (requires you to supply the full endpoint (`yoururl.tld/apis/listenbrainz/1/submit-listens`))
|
||||||
|
|
||||||
I'm thankful for any feedback whether other scrobblers work!
|
I'm thankful for any feedback whether other scrobblers work!
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from . import native_v1
|
from . import native_v1
|
||||||
from .audioscrobbler import Audioscrobbler
|
from .audioscrobbler import Audioscrobbler
|
||||||
|
from .audioscrobbler_legacy import AudioscrobblerLegacy
|
||||||
from .listenbrainz import Listenbrainz
|
from .listenbrainz import Listenbrainz
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
@ -11,7 +12,8 @@ native_apis = [
|
|||||||
]
|
]
|
||||||
standardized_apis = [
|
standardized_apis = [
|
||||||
Listenbrainz(),
|
Listenbrainz(),
|
||||||
Audioscrobbler()
|
Audioscrobbler(),
|
||||||
|
AudioscrobblerLegacy()
|
||||||
]
|
]
|
||||||
|
|
||||||
def init_apis(server):
|
def init_apis(server):
|
||||||
|
107
maloja/apis/audioscrobbler_legacy.py
Normal file
107
maloja/apis/audioscrobbler_legacy.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
from ._base import APIHandler
|
||||||
|
from ._exceptions import *
|
||||||
|
from .. import database
|
||||||
|
|
||||||
|
class AudioscrobblerLegacy(APIHandler):
|
||||||
|
__apiname__ = "Legacy Audioscrobbler"
|
||||||
|
__doclink__ = "https://web.archive.org/web/20190531021725/https://www.last.fm/api/submissions"
|
||||||
|
__aliases__ = [
|
||||||
|
"audioscrobbler_legacy",
|
||||||
|
"audioscrobbler/1.2"
|
||||||
|
]
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
|
||||||
|
# no need to save these on disk, clients can always request a new session
|
||||||
|
self.mobile_sessions = []
|
||||||
|
self.methods = {
|
||||||
|
"handshake":self.handshake,
|
||||||
|
"nowplaying":self.now_playing,
|
||||||
|
"scrobble":self.submit_scrobble
|
||||||
|
}
|
||||||
|
self.errors = {
|
||||||
|
BadAuthException:(200,"BADAUTH"),
|
||||||
|
InvalidAuthException:(200,"BADAUTH"),
|
||||||
|
InvalidMethodException:(200,"FAILED"),
|
||||||
|
InvalidSessionKey:(200,"BADSESSION"),
|
||||||
|
ScrobblingException:(500,"FAILED")
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_method(self,pathnodes,keys):
|
||||||
|
if keys.get("hs") == 'true': return 'handshake'
|
||||||
|
else: return pathnodes[0]
|
||||||
|
|
||||||
|
def handshake(self,pathnodes,keys):
|
||||||
|
user = keys.get("u")
|
||||||
|
auth = keys.get("a")
|
||||||
|
timestamp = keys.get("t")
|
||||||
|
apikey = keys.get("api_key")
|
||||||
|
host = keys.get("Host")
|
||||||
|
protocol = 'https'
|
||||||
|
# expect username and password
|
||||||
|
if auth is not None:
|
||||||
|
for key in database.allAPIkeys():
|
||||||
|
if check_token(auth, key, timestamp):
|
||||||
|
sessionkey = generate_key(self.mobile_sessions)
|
||||||
|
return 200, (
|
||||||
|
"OK\n"
|
||||||
|
f"{sessionkey}\n"
|
||||||
|
f"{protocol}://{host}/apis/audioscrobbler_legacy/nowplaying\n"
|
||||||
|
f"{protocol}://{host}/apis/audioscrobbler_legacy/scrobble\n"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise InvalidAuthException()
|
||||||
|
else:
|
||||||
|
raise BadAuthException()
|
||||||
|
|
||||||
|
def now_playing(self,pathnodes,keys):
|
||||||
|
# I see no implementation in the other compatible APIs, so I have just
|
||||||
|
# created a route that always says it was successful except if the
|
||||||
|
# session is invalid
|
||||||
|
if keys.get("s") is None or keys.get("s") not in self.mobile_sessions:
|
||||||
|
raise InvalidSessionKey()
|
||||||
|
else:
|
||||||
|
return "OK"
|
||||||
|
|
||||||
|
def submit_scrobble(self,pathnodes,keys):
|
||||||
|
if keys.get("s") is None or keys.get("s") not in self.mobile_sessions:
|
||||||
|
raise InvalidSessionKey()
|
||||||
|
else:
|
||||||
|
for count in range(0,50):
|
||||||
|
artist_key = f"a[{count}]"
|
||||||
|
track_key = f"t[{count}]"
|
||||||
|
time_key = f"i[{count}]"
|
||||||
|
if artist_key in keys and track_key in keys:
|
||||||
|
artiststr,titlestr = keys[artist_key], keys[track_key]
|
||||||
|
try:
|
||||||
|
timestamp = int(keys[time_key])
|
||||||
|
except:
|
||||||
|
timestamp = None
|
||||||
|
#database.createScrobble(artists,title,timestamp)
|
||||||
|
self.scrobble(artiststr,titlestr,time=timestamp)
|
||||||
|
else:
|
||||||
|
return 200,"OK"
|
||||||
|
return 200,"OK"
|
||||||
|
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import random
|
||||||
|
|
||||||
|
def md5(input):
|
||||||
|
m = hashlib.md5()
|
||||||
|
m.update(bytes(input,encoding="utf-8"))
|
||||||
|
return m.hexdigest()
|
||||||
|
|
||||||
|
def generate_key(ls):
|
||||||
|
key = ""
|
||||||
|
for i in range(64):
|
||||||
|
key += str(random.choice(list(range(10)) + list("abcdefghijklmnopqrstuvwxyz") + list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")))
|
||||||
|
ls.append(key)
|
||||||
|
return key
|
||||||
|
|
||||||
|
def lastfm_token(password, ts):
|
||||||
|
return md5(md5(password) + ts)
|
||||||
|
|
||||||
|
def check_token(received_token, expected_key, ts):
|
||||||
|
expected_token = lastfm_token(expected_key, ts)
|
||||||
|
return received_token == expected_token
|
@ -308,6 +308,16 @@
|
|||||||
],
|
],
|
||||||
"type": "text/javascript"
|
"type": "text/javascript"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"listen": "prerequest",
|
||||||
|
"script": {
|
||||||
|
"id": "9928c378-cf37-4e20-b653-51f5dde51192",
|
||||||
|
"exec": [
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"type": "text/javascript"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"request": {
|
"request": {
|
||||||
@ -389,7 +399,7 @@
|
|||||||
{
|
{
|
||||||
"listen": "test",
|
"listen": "test",
|
||||||
"script": {
|
"script": {
|
||||||
"id": "28214541-89bf-4184-ad9b-dd49dbcfc35d",
|
"id": "addc7f42-1de5-4b6d-a840-bb3075bd2cdc",
|
||||||
"exec": [
|
"exec": [
|
||||||
"var data = JSON.parse(responseBody);",
|
"var data = JSON.parse(responseBody);",
|
||||||
"postman.setEnvironmentVariable(\"session_key\", data.session.key);",
|
"postman.setEnvironmentVariable(\"session_key\", data.session.key);",
|
||||||
@ -430,6 +440,113 @@
|
|||||||
"response": []
|
"response": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Scrobble Audioscrobbler Legacy",
|
||||||
|
"item": [
|
||||||
|
{
|
||||||
|
"name": "Authorize",
|
||||||
|
"event": [
|
||||||
|
{
|
||||||
|
"listen": "test",
|
||||||
|
"script": {
|
||||||
|
"id": "01f6143f-3134-4006-9792-6e61a2be323d",
|
||||||
|
"exec": [
|
||||||
|
"var data = responseBody.split(\"\\n\");",
|
||||||
|
"postman.setEnvironmentVariable(\"session_key\", data[1]);"
|
||||||
|
],
|
||||||
|
"type": "text/javascript"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"listen": "prerequest",
|
||||||
|
"script": {
|
||||||
|
"id": "b97afa75-ab8c-4099-a6cf-6b45d653a10d",
|
||||||
|
"exec": [
|
||||||
|
"apikey = pm.variables.get(\"api_key\");",
|
||||||
|
"ts = 565566;",
|
||||||
|
"",
|
||||||
|
"token = CryptoJS.MD5(CryptoJS.MD5(apikey) + ts)",
|
||||||
|
"",
|
||||||
|
"postman.setEnvironmentVariable(\"legacy_token\", token);"
|
||||||
|
],
|
||||||
|
"type": "text/javascript"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"protocolProfileBehavior": {
|
||||||
|
"disableBodyPruning": true
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "{{url}}/apis/audioscrobbler_legacy/?hs=true&t=565566&a={{legacy_token}}",
|
||||||
|
"host": [
|
||||||
|
"{{url}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"apis",
|
||||||
|
"audioscrobbler_legacy",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"query": [
|
||||||
|
{
|
||||||
|
"key": "hs",
|
||||||
|
"value": "true"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "t",
|
||||||
|
"value": "565566"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "a",
|
||||||
|
"value": "{{legacy_token}}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Scrobble",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [],
|
||||||
|
"url": {
|
||||||
|
"raw": "{{url}}/apis/audioscrobbler_legacy/scrobble?t=565566&a={{legacy_token}}&s={{session_key}}",
|
||||||
|
"host": [
|
||||||
|
"{{url}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"apis",
|
||||||
|
"audioscrobbler_legacy",
|
||||||
|
"scrobble"
|
||||||
|
],
|
||||||
|
"query": [
|
||||||
|
{
|
||||||
|
"key": "t",
|
||||||
|
"value": "565566"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "a",
|
||||||
|
"value": "{{legacy_token}}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "s",
|
||||||
|
"value": "{{session_key}}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"protocolProfileBehavior": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"event": [
|
"event": [
|
||||||
@ -456,28 +573,34 @@
|
|||||||
],
|
],
|
||||||
"variable": [
|
"variable": [
|
||||||
{
|
{
|
||||||
"id": "0206e63b-eeb7-49cc-9824-5398b18f7736",
|
"id": "3e20a0c6-11fa-4976-8bcb-5c31014e40e7",
|
||||||
"key": "url",
|
"key": "url",
|
||||||
"value": "http://localhost:42010",
|
"value": "http://localhost:42010"
|
||||||
"type": "string"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "0c6402d8-dfb7-4c87-a6ca-9b6675b8d9a1",
|
"id": "bd31b51f-645d-4ab4-83e1-8eb407978ea8",
|
||||||
"key": "api_key",
|
"key": "api_key",
|
||||||
"value": "localdevtestkey",
|
"value": "localdevtestkey"
|
||||||
"type": "string"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bae7cf4e-fe0e-490d-8446-56a8ac51373d",
|
"id": "5ea9cbf8-34f9-4c5e-80b3-42857f014f80",
|
||||||
"key": "example_artist",
|
"key": "example_artist",
|
||||||
"value": "EXID ft. Jeremy Soule",
|
"value": "EXID ft. Jeremy Soule"
|
||||||
"type": "string"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "70454e83-de63-471b-a58c-8545cef4e749",
|
"id": "fa4d0af7-6f09-4fc6-88ee-39cb6b91b844",
|
||||||
"key": "example_song",
|
"key": "example_song",
|
||||||
"value": "Why is the Rum gone?",
|
"value": "Why is the Rum gone?"
|
||||||
"type": "string"
|
},
|
||||||
|
{
|
||||||
|
"id": "e078ab40-4135-4be3-a251-9df21b2601c1",
|
||||||
|
"key": "example_artist_2",
|
||||||
|
"value": "BLACKPINK ft. Tzuyu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3748cc0f-2bdc-4572-8b17-94a630fa751c",
|
||||||
|
"key": "example_song_2",
|
||||||
|
"value": "POP/STARS"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user