Skip to content

Commit d2e77f5

Browse files
committed
feat: add coverart support
1 parent 4bc641e commit d2e77f5

3 files changed

Lines changed: 104 additions & 1 deletion

File tree

mopidy_subidy/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,10 @@ def get_config_schema(self):
2727

2828
def setup(self, registry):
2929
from .backend import SubidyBackend
30+
from .web import image_proxy_factory
3031

3132
registry.add("backend", SubidyBackend)
33+
registry.add("http:app", {
34+
"name": self.ext_name,
35+
"factory": image_proxy_factory,
36+
})

mopidy_subidy/library.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22

33
from mopidy import backend
4-
from mopidy.models import Ref, SearchResult
4+
from mopidy.models import Image, Ref, SearchResult
55
from mopidy_subidy import uri
66

77
logger = logging.getLogger(__name__)
@@ -208,3 +208,13 @@ def search(self, query=None, uris=None, exact=False):
208208
if "any" in query:
209209
return self.subsonic_api.find_as_search_result(query.get("any")[0])
210210
return SearchResult(artists=self.subsonic_api.get_artists_as_artists())
211+
212+
def get_images(self, uris):
213+
images = {}
214+
for uri in uris:
215+
# TODO: derive hostname
216+
if uri.startswith("subidy:album:"):
217+
images[uri] = [Image(uri=f"http://localhost:6680/subidy/coverart/2{uri[14:]}")]
218+
elif uri.startswith("subidy:song:"):
219+
images[uri] = [Image(uri=f"http://localhost:6680/subidy/coverart/3{uri[13:]}")]
220+
return images

mopidy_subidy/web.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import os
2+
import logging
3+
4+
import tornado.web
5+
import tornado.httpclient
6+
7+
from urllib.parse import urlparse
8+
9+
logger = logging.getLogger('subidy-web')
10+
11+
12+
def image_proxy_factory(config, core):
13+
return [
14+
(r"/coverart/(?P<id>.+)", ImageProxyHandler, {"core": core, "config": config})
15+
]
16+
17+
class ImageProxyHandler(tornado.web.RequestHandler):
18+
def initialize(self, core, config):
19+
self.core = core
20+
21+
subidy_config = config["subidy"]
22+
self._subsonic_url=subidy_config["url"]
23+
self._subsonic_username=subidy_config["username"]
24+
self._subsonic_password=subidy_config["password"]
25+
26+
async def get(self, **kwargs):
27+
id = kwargs.get('id')
28+
29+
logger.debug('Handle coverart %s request', id)
30+
31+
def handle_response(response):
32+
if (response.error and not
33+
isinstance(response.error, tornado.httpclient.HTTPError)):
34+
self.set_status(500)
35+
self.write('Internal server error:\n' + str(response.error))
36+
else:
37+
self.set_status(response.code, response.reason)
38+
self._headers = tornado.httputil.HTTPHeaders() # clear tornado default header
39+
40+
for header, v in response.headers.get_all():
41+
if header not in ('Content-Length', 'Transfer-Encoding', 'Content-Encoding', 'Connection'):
42+
self.add_header(header, v) # some header appear multiple times, eg 'Set-Cookie'
43+
44+
if response.body:
45+
self.set_header('Content-Length', len(response.body))
46+
self.write(response.body)
47+
self.finish()
48+
49+
try:
50+
if 'Proxy-Connection' in self.request.headers:
51+
del self.request.headers['Proxy-Connection']
52+
resp = await fetch_request(
53+
f"{self._subsonic_url}/rest/getCoverArt?id={id}",
54+
method=self.request.method, headers=self.request.headers,
55+
follow_redirects=False, allow_nonstandard_methods=False)
56+
handle_response(resp)
57+
except tornado.httpclient.HTTPError as e:
58+
if hasattr(e, 'response') and e.response:
59+
handle_response(e.response)
60+
else:
61+
self.set_status(500)
62+
self.write('Internal server error:\n' + str(e))
63+
self.finish()
64+
65+
def get_proxy(url):
66+
url_parsed = urlparse(url, scheme='http')
67+
proxy_key = '%s_proxy' % url_parsed.scheme
68+
return os.environ.get(proxy_key)
69+
70+
71+
def parse_proxy(proxy):
72+
proxy_parsed = urlparse(proxy, scheme='http')
73+
return proxy_parsed.hostname, proxy_parsed.port
74+
75+
76+
def fetch_request(url, **kwargs):
77+
proxy = get_proxy(url)
78+
if proxy:
79+
logger.debug('Forward request via upstream proxy %s', proxy)
80+
tornado.httpclient.AsyncHTTPClient.configure(
81+
'tornado.curl_httpclient.CurlAsyncHTTPClient')
82+
host, port = parse_proxy(proxy)
83+
kwargs['proxy_host'] = host
84+
kwargs['proxy_port'] = port
85+
86+
req = tornado.httpclient.HTTPRequest(url, **kwargs)
87+
client = tornado.httpclient.AsyncHTTPClient()
88+
return client.fetch(req, raise_error=False)

0 commit comments

Comments
 (0)