a b/src/cdplugins/tidal/tidal.py
1
#!/usr/bin/env python
2
# Copyright (C) 2016 J.F.Dockes
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the
15
# Free Software Foundation, Inc.,
16
# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17
18
from __future__ import print_function
19
20
import sys
21
import os
22
import json
23
import re
24
import conftree
25
import cmdtalkplugin
26
import tidalapi
27
from tidalapi.models import Album, Artist
28
from tidalapi import Quality
29
30
routes = cmdtalkplugin.Routes()
31
processor = cmdtalkplugin.Processor(routes)
32
33
is_logged_in = False
34
35
def maybelogin():
36
    global session
37
    global quality
38
    
39
    if is_logged_in:
40
        return True
41
42
    if "UPMPD_CONFIG" not in os.environ:
43
        raise Exception("No UPMPD_CONFIG in environment")
44
    upconfig = conftree.ConfSimple(os.environ["UPMPD_CONFIG"])
45
    
46
    username = upconfig.get('tidaluser')
47
    password = upconfig.get('tidalpass')
48
    qalstr = upconfig.get('tidalquality')
49
    if not username or not password:
50
        raise Exception("tidaluser and/or tidalpass not set in configuration")
51
52
    if qalstr == 'lossless':
53
        quality = Quality.lossless
54
    elif qalstr == 'high':
55
        quality = Quality.high
56
    elif qalstr == 'low':
57
        quality = Quality.low
58
    elif not qalstr:
59
        quality = Quality.high
60
    else:
61
        raise Exception("Bad tidal quality string in config. "+
62
                        "Must be lossless/high/low")
63
    tidalconf = tidalapi.Config(quality)
64
    session = tidalapi.Session(config=tidalconf)
65
    session.login(username, password)
66
67
68
69
def trackentries(objid, tracks):
70
    entries = []
71
    for track in tracks:
72
        if not track.available:
73
            continue
74
        li = {}
75
        
76
77
        li['pid'] = objid
78
        li['id'] = objid + '$' + "%s"%track.id
79
        li['tt'] = track.name
80
        li['uri'] = 'http://s.com/upmpcli-tidal/track?version=1&trackId=%s' % track.id
81
        li['tp'] = 'it'
82
        if track.album:
83
            li['upnp:album'] = track.album.name
84
            if track.album.image:
85
                li['upnp:albumArtUri'] = track.album.image
86
        li['upnp:originalTrackNumber'] =  str(track.track_num)
87
        li['upnp:artist'] = track.artist.name
88
        li['dc:creator'] = track.artist.name
89
        li['dc:title'] = track.name
90
        li['discnumber'] = str(track.disc_num)
91
        li['duration'] = track.duration
92
        # track.album.release_date.year if track.album.release_date else None,
93
94
        entries.append(li)
95
        return entries
96
97
98
def direntry(id, pid, title):
99
    return {'id': id, 'pid' : pid, 'tt':title, 'tp':'ct'}
100
101
def direntries(objid, ttidlist):
102
    content = []
103
    for tt,id in ttidlist:
104
        content.append(direntry(objid+'$'+id, objid, tt))
105
    return content
106
107
@routes.route('browse')
108
def browse(a):
109
    processor.rclog("browse: [%s]" % a)
110
    if 'objid' not in a:
111
        raise Exception("No objid in args")
112
    objid = a['objid']
113
    if re.match('0\$tidal', objid) is None:
114
        raise Exception("bad objid [%s]" % objid)
115
116
    maybelogin()
117
    if objid == '0$tidal':
118
        # Root
119
        content = direntries(objid,
120
                              (('My Music', 'my_music'),
121
                               ('Featured Playlists', 'featured'),
122
                               ("What's New", 'new'),
123
                               ('Genres', 'genres'),
124
                               ('Moods', 'moods'),
125
                               ('Search', 'search')))
126
    elif objid == '0$tidal$my_music':
127
        content = direntries(objid,
128
                             (('My Playlists', 'my_pl'),
129
                              ('Favourite Playlists', 'fav_pl'),
130
                              ('Favourite Artists', 'fav_art'),
131
                              ('Favourite Albums', 'fav_alb'),
132
                              ('Favourite Tracks', 'fav_trk')))
133
    elif objid == '0$tidal$my_music$fav_trk':
134
        content = trackentries(objid, session.user.favorites.tracks())
135
    elif objid == '0$tidal$my_music$my_pl':
136
        items = session.user.playlists()
137
        view(items, urls_from_id(playlist_view, items))
138
    elif objid == '0$tidal$my_music$fav_pl':
139
        items = session.user.favorites.playlists()
140
        view(items, urls_from_id(playlist_view, items))
141
    elif objid == '0$tidal$my_music$fav_art':
142
        xbmcplugin.setContent(plugin.handle, 'artists')
143
        items = session.user.favorites.artists()
144
        view(items, urls_from_id(artist_view, items))
145
    elif objid == '0$tidal$my_music$fav_alb':
146
        xbmcplugin.setContent(plugin.handle, 'albums')
147
        items = session.user.favorites.albums()
148
        view(items, urls_from_id(album_view, items))
149
    else:
150
        raise Exception("unhandled path")
151
152
    encoded = json.dumps(content)
153
    return {"entries":encoded}
154
155
156
157
processor.rclog("Tidal running")
158
processor.mainloop()