Skip to content

Instantly share code, notes, and snippets.

@secondwtq
Created August 20, 2018 12:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save secondwtq/d614012c8f31c03c8382665bdb15738c to your computer and use it in GitHub Desktop.
Save secondwtq/d614012c8f31c03c8382665bdb15738c to your computer and use it in GitHub Desktop.
{-# START_FILE ./.gitignore #-}
.DS_Store
.idea
*.log
tmp/
*.py[cod]
*.egg
build
htmlcov
{-# START_FILE ./hsca.hs #-}
import Hscaffold(hscaffoldFromDirectoryWith, toHsfiles)
import Control.Monad.Writer(writer)
import Data.Text(unpack)
doFilter :: [FilePath] -> [FilePath]
doFilter paths =
filter f paths
where
f a =
case a of
"." -> False
".." -> False
".DS_Store" -> False
_ -> True
main :: IO ()
main = do
scaffold <- hscaffoldFromDirectoryWith doFilter "."
let hsfiles = toHsfiles $ writer ((), scaffold)
putStrLn $ unpack hsfiles
{-# START_FILE ./hsct.hs #-}
import Hscaffold(runHscaffold, fromHsfiles)
import Control.Monad.Writer(writer)
import Data.Text(unpack)
main :: IO ()
main = do
hsfiles <- readFile "a.txt"
let scaffold = fromHsfiles hsfiles
runHscaffold "." (writer ((), scaffold))
{-# START_FILE ./MANIFEST.in #-}
include MANIFEST.in
include mopidy_qobuz/ext.conf
{-# START_FILE ./mopidy_qobuz/__init__.py #-}
from __future__ import unicode_literals
from mopidy import config, ext
from os import path
__version__ = "0.0.1"
class Extension(ext.Extension):
dist_name = "Mopidy-Qobuz"
ext_name = "qobuz"
version = __version__
def get_default_config(self):
return config.read(path.join(path.dirname(__file__), "ext.conf"))
def get_config_schema(self):
schema = super(Extension, self).get_config_schema()
schema["username"] = config.String()
schema["password"] = config.Secret()
schema["client_id"] = config.String()
schema["client_secret"] = config.Secret()
return schema
def validate_config(self, config):
if not config.getboolean("qobuz", "enabled"):
return
def setup(self, registry):
from mopidy_qobuz.backend import QobuzBackend
registry.add("backend", QobuzBackend)
{-# START_FILE ./mopidy_qobuz/backend.py #-}
from __future__ import unicode_literals
import logging
import os
from mopidy import backend, httpclient
import client
import pykka
class QobuzBackend(pykka.ThreadingActor, backend.Backend):
def __init__(self, config, audio):
super(QobuzBackend, self).__init__()
self._config = config
self._audio = audio
self.library = client.QobuzLibraryProvider(backend=self)
self.uri_schemes = ["qobuz"]
self.session = None
def on_start(self):
self.session = client.get_requests_session(self._config["qobuz"])
{-# START_FILE ./mopidy_qobuz/client.py #-}
from __future__ import absolute_import, unicode_literals
import requests
import urlparse
import logging
import hashlib
from mopidy import backend, models
import types
import time
import re
# URI:
# qobuz:track:$id
API_PREFIX = "http://www.qobuz.com/api.json/0.2/"
REGEX_URL = "http://www\.qobuz\.com/api\.json/0\.2/(.*)"
USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"
URI_ROOT = "qobuz:directory"
URI_ALBUMS = "qobuz:album"
logger = logging.getLogger(__name__)
def api(p):
return urlparse.urljoin(API_PREFIX, p)
def to_album_ref(album):
return models.Ref.album(uri="%s:%s" % (URI_ALBUMS, album["id"]),
name=album["title"])
class QobuzRequestsSession(requests.Session):
def __init__(self, app_id, app_secret, user_auth_token):
super(QobuzRequestsSession, self).__init__()
self._app_id = app_id
self._app_secret = app_secret
self._user_auth_token = user_auth_token
self.headers.update({
"User-Agent": USER_AGENT,
"X-App-Id": app_id,
"X-User-Auth-Token": user_auth_token,
})
def prepare_request(self, request):
if request.method == "GET":
path = re.search(REGEX_URL, request.url).groups()[0].replace('/','')
params = "".join([(k + v) for k, v in request.params.items()])
ts = long(time.time())
sigstr = "{path}{params}{ts}{secret}".format(
path=path, params=params, ts=ts, secret=self._app_secret)
sigdigest = hashlib.md5(sigstr).hexdigest()
logger.debug("Signing request %s %s", sigstr, sigdigest)
request.params["request_ts"] = ts
request.params["request_sig"] = sigdigest
p = requests.Session.prepare_request(self, request)
return p
def get_requests_session(config):
# TODO: proxy
login = requests.get(api("user/login"), params={
"app_id": config["client_id"],
"email": config["username"],
"password": hashlib.md5(config["password"]).hexdigest(),
}, headers={ "user-agent": USER_AGENT })
if login.status_code != requests.codes.ok:
pass
# TODO: add log
login_data = login.json()
logger.debug("Successful logged in %s %s",
login_data["user"]["credential"]["description"],
login_data["user"]["email"])
session = QobuzRequestsSession(config["client_id"],
config["client_secret"], login_data["user_auth_token"])
return session
class QobuzPlaybackProvider(backend.PlaybackProvider):
def translate_uri(self, uri):
return None
class QobuzLibraryProvider(backend.LibraryProvider):
root_directory = models.Ref.directory(
uri=URI_ROOT,
name="Qobuz",
)
def __init__(self, backend):
super(QobuzLibraryProvider, self).__init__(backend)
self._root = [
models.Ref.directory(uri=URI_ALBUMS, name="Albums"),
]
def browse(self, uri):
logger.debug("Browsing URI %s", uri)
if uri == URI_ROOT:
return self._root
elif uri == URI_ALBUMS:
return self._browse_albums()
return []
def lookup(self, uri):
logger.debug("Looking up URI %s", uri)
return []
def _browse_albums(self):
# TODO: paging
album_list_req = self.backend.session.get(api("userLibrary/getAlbumsList"))
album_list = album_list_req.json()["items"]
return map(to_album_ref, album_list)
{-# START_FILE ./mopidy_qobuz/ext.conf #-}
[qobuz]
enabled = true
username =
password =
client_id =
client_secret =
{-# START_FILE ./Mopidy_Qobuz.egg-info/dependency_links.txt #-}
{-# START_FILE ./Mopidy_Qobuz.egg-info/entry_points.txt #-}
[mopidy.ext]
qobuz = mopidy_qobuz:Extension
{-# START_FILE ./Mopidy_Qobuz.egg-info/not-zip-safe #-}
{-# START_FILE ./Mopidy_Qobuz.egg-info/PKG-INFO #-}
Metadata-Version: 1.0
Name: Mopidy-Qobuz
Version: 0.0.1
Summary: Mopidy extension for playing music from Qobuz
Home-page: https://github.com/secondwtq/mopidy-qobuz
Author: Second Datke
Author-email: lovejay-lovemusic@outlook.com
License: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN
{-# START_FILE ./Mopidy_Qobuz.egg-info/requires.txt #-}
Mopidy>=2.0
Pykka>=1.1
requests>=2.0
setuptools
{-# START_FILE ./Mopidy_Qobuz.egg-info/SOURCES.txt #-}
MANIFEST.in
setup.py
Mopidy_Qobuz.egg-info/PKG-INFO
Mopidy_Qobuz.egg-info/SOURCES.txt
Mopidy_Qobuz.egg-info/dependency_links.txt
Mopidy_Qobuz.egg-info/entry_points.txt
Mopidy_Qobuz.egg-info/not-zip-safe
Mopidy_Qobuz.egg-info/requires.txt
Mopidy_Qobuz.egg-info/top_level.txt
mopidy_qobuz/__init__.py
mopidy_qobuz/backend.py
mopidy_qobuz/client.py
mopidy_qobuz/ext.conf
{-# START_FILE ./Mopidy_Qobuz.egg-info/top_level.txt #-}
mopidy_qobuz
{-# START_FILE ./setup.py #-}
from __future__ import unicode_literals
from setuptools import find_packages, setup
import re
def get_version(filename):
regex = "__([a-z]+)__ = \"([^\"]+)\""
return dict(re.findall(regex, open(filename).read()))["version"]
setup(name="Mopidy-Qobuz",
version=get_version("mopidy_qobuz/__init__.py"),
description="Mopidy extension for playing music from Qobuz",
url="https://github.com/secondwtq/mopidy-qobuz",
author="Second Datke",
author_email="lovejay-lovemusic@outlook.com",
install_requires=[
"Mopidy >= 2.0",
"Pykka >= 1.1",
"requests >= 2.0",
"setuptools"
],
entry_points={
"mopidy.ext": [
"qobuz = mopidy_qobuz:Extension"
]
},
packages=find_packages(),
zip_safe=False,
include_package_data=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment