#!/usr/bin/env python3
# vim: set ts=4 sw=4 expandtab syntax=python :

import os
import os.path
import html
import json
import hashlib
import textwrap
import urllib.parse as up
import urllib.request as urq

from datetime import datetime, timezone
from pathlib import Path


def loadConfig(cfgfile = None):
    if cfgfile is None or cfgfile == '':
        cfgdir = Path(os.path.expanduser(os.getenv('XDG_CONFIG_DIR', '~/.config')))
        cfgfile = cfgdir / 'iris-subsonic.json'

    cfgfile = Path(cfgfile)
    if not cfgfile.is_file():
        raise RuntimeError(f"Configuration file {cfgfile!r} does not exist")

    with open(cfgfile, 'r') as fh:
        return json.loads(

def hashPassword(password):
    salt = hashlib.md5(os.urandom(24)).hexdigest()
    hashed = hashlib.md5((password + salt).encode('utf-8')).hexdigest()
    return (salt, hashed)

def apiGet(slug, params = {}):
    if not 'u' in params:
        params['u'] = CONFIG['user']
    if not 'p' in params:
        params['p'] = CONFIG['pass']
    if 'p' in params:
        s, t = hashPassword(params['p'])
        params['s'] = s
        params['t'] = t
        del params['p']

    params['f'] = 'json'
    params['c'] = 'iris-subsonic/0.1'
    params['v'] = '1.16.1'

    url = CONFIG['host'] + slug + '?' + up.urlencode(params)
    with urq.urlopen(url) as resp:
        res = json.loads(

    if 'subsonic-response' in res and 'status' in res['subsonic-response']:
        if res['subsonic-response']['status'] == 'ok':
            return res['subsonic-response']

    status = res['subsonic-response']['status']
    raise RuntimeError(f"Subsonic API returned non-ok status for {slug!r}: {status!r}")

def apiPing(params = {}):
    return apiGet('/rest/ping', params)

def apiAlbumList(params = {}):
    if not 'type' in params:
        params['type'] = 'alphabeticalByArtist'
    if not 'size' in params:
        params['size'] = 500

    albums, coffset = ([], 0)
    while True:
        params['offset'] = coffset
        res = apiGet('/rest/getAlbumList2', params)
        if 'album' not in res['albumList2'] or len(res['albumList2']['album']) == 0:

        albums += res['albumList2']['album']
        coffset += len(res['albumList2']['album'])

    return albums

def apiAlbumInfo(params = {}):
    # XXX: this is not implemented in Navidrome
    if SERVER_TYPE in ['navidrome']:
        raise RuntimeError(f"/rest/getAlbumInfo2 not implemented on server type {SERVER_TYPE!r}")

    res = apiGet('/rest/getAlbumInfo2', params)
    if not 'albumInfo' in res:
        return None
    return res['albumInfo']

def renderAlbums():
    albumelements = []
    albums = apiAlbumList({'type': 'alphabeticalByArtist', 'size': 500})
    for entity in albums:
        album_info = {}
            album_info = apiAlbumInfo({'id': entity['id']})
            album_info = {}

        bracketed = []
        n_album = html.escape(entity['album'])
        n_artist = html.escape(entity['artist'])

        # if we have a MusicBrainz release ID, include a link to it
        if 'musicBrainzId' in album_info and album_info['musicBrainzId'] is not None:
            mbuuid = album_info['musicBrainzId']
            mblink = html.escape(f"{mbuuid}")
            bracketed.append(f"<a href=\"{mblink}\" target=\"_blank\">musicbrainz</a>")

        bracketed = ", ".join(bracketed)
        if len(bracketed.strip()) > 0:
            bracketed = f" ({bracketed})"

        albumelements.append(f"<li><strong>{n_album}</strong> - {n_artist}{bracketed}</li>")

    return albumelements

def main():
    global CONFIG
    CONFIG = loadConfig(os.getenv('IRIS_SUBSONIC_CONFIG', None))

    global SERVER_TYPE
    pingres = apiPing()
    SERVER_TYPE = pingres['type']
    # template variables!
    host = html.escape(CONFIG['host'])
    ts = html.escape("%Y-%m-%d %H:%M:%S %Z"))
    albumelements = renderAlbums()
    albumcount = len(albumelements)
    albumelements = textwrap.indent("\n".join(albumelements), '    ' * 3).strip()

    # template
    output = textwrap.dedent(f"""\
    <!DOCTYPE html>
    <html prefix="og:">
        <meta charset="utf-8">
        <meta name="viewport" content="initial-scale=1, width=device-width">
        <meta property="og:title" content="Album list for {host}">
        <meta property="og:description" content="As of {ts}, this server has {albumcount} albums.">
        <title>Album list for {host}</title>
            *, *::before, *::after {{ box-sizing: border-box; }}
            html, body {{ padding: 0; margin: 0; font-family: sans-serif; }}
            body {{ padding: 1rem; }}
            Album list for
            <a href="{host}" target="_blank">{host}</a>
            There is a total of
            albums on this server.
        <ul class="album-list">
            List generated at {ts} by
            <a href="" target="_blank"></a> :)

    # and print to stdout :)
    return 0

if __name__ == "__main__":