Switch to unified view

a/src/mediaserver/cdplugins/uprcl/uprclutils.py b/src/mediaserver/cdplugins/uprcl/uprclutils.py
1
from __future__ import print_function
1
from __future__ import print_function
2
2
3
import sys
3
import sys
4
import posixpath
4
import posixpath
5
import urllib
5
import urllib
6
import os
6
7
7
audiomtypes = frozenset([
8
audiomtypes = frozenset([
8
    'audio/mpeg',
9
    'audio/mpeg',
9
    'application/x-flac',
10
    'application/x-flac',
10
    'application/ogg',
11
    'application/ogg',
11
    'audio/aac',
12
    'audio/aac',
12
    'audio/mp4',
13
    'audio/mp4',
13
    'audio/x-aiff',
14
    'audio/x-aiff',
14
    'audio/x-wav'
15
    'audio/x-wav',
16
    'inode/directory'
15
    ])
17
    ])
16
18
19
upnp2rclfields = {'upnp:album': 'album',
20
                  'releasedate' : 'date',
21
                  'upnp:originalTrackNumber' : 'tracknumber',
22
                  'upnp:artist' : 'artist',
23
                  'upnp:genre' : 'genre',
24
                  'res:mime' : 'mtype',
25
                  'duration' : 'duration',
26
                  'res:samplefreq' : 'sample_rate'
27
                  }
28
    
17
def rcldoctoentry(id, pid, httphp, pathprefix, doc):
29
def rcldoctoentry(id, pid, httphp, pathprefix, doc):
18
    """
30
    """
19
    Transform a Doc objects into the format expected by the parent
31
    Transform a Doc objects into the format expected by the parent
20
32
21
    Args:
33
    Args:
...
...
36
        configured host:port and pathprefix arguments and track Id:
48
        configured host:port and pathprefix arguments and track Id:
37
        TBD
49
        TBD
38
            http://host:port/pathprefix/track?version=1&trackId=<trackid>
50
            http://host:port/pathprefix/track?version=1&trackId=<trackid>
39
    
51
    
40
    """
52
    """
41
    uplog("rcldoctoentry:  pid %s id %s httphp %s pathprefix %s" %
53
    uplog("rcldoctoentry:  pid %s id %s mtype %s" %
42
          (pid, id, httphp, pathprefix))
54
          (pid, id, doc.mtype))
43
    
55
    
44
    li = {}
56
    li = {}
45
    if doc.mtype not in audiomtypes:
57
    if doc.mtype not in audiomtypes:
46
        return li
58
        return li
47
59
48
    li['pid'] = pid
60
    li['pid'] = pid
49
    li['id'] = id
61
    li['id'] = id
50
    li['tp'] = 'it'
62
    li['tp'] = 'ct' if doc.mtype == 'inode/directory' else 'it'
51
    # Why no dc.title??
63
    # Why no dc.title??
52
    li['tt'] = doc.title
64
    li['tt'] = doc.title
53
65
54
    # TBD
66
    # TBD
55
    li['upnp:class'] = 'object.item.audioItem.musicTrack'
67
    li['upnp:class'] = 'object.item.audioItem.musicTrack'
...
...
70
    #discnumber=
82
    #discnumber=
71
    #genre=
83
    #genre=
72
    #lyricist=
84
    #lyricist=
73
    #lyrics=
85
    #lyrics=
74
86
75
    for oname,dname in [('upnp:album', 'album'), ('releasedate','date'),
87
    for oname,dname in upnp2rclfields.iteritems():
76
                        ('upnp:originalTrackNumber', 'tracknumber'),
77
                        ('upnp:artist', 'artist'), ('upnp:genre', 'genre'),
78
                        ('res:mime', 'mtype'), ('duration', 'duration'),
79
                        ('res:samplefreq', 'sample_rate')]:
80
        val = getattr(doc, dname)
88
        val = getattr(doc, dname)
81
        if val:
89
        if val:
82
            li[oname] = val
90
            li[oname] = val
83
91
84
    try:
92
    try:
...
...
96
    path = pathprefix + path
104
    path = pathprefix + path
97
    li['uri'] = "http://%s%s" % (httphp, urllib.quote(path))
105
    li['uri'] = "http://%s%s" % (httphp, urllib.quote(path))
98
    uplog("rcldoctoentry: uri: %s" % li['uri'])
106
    uplog("rcldoctoentry: uri: %s" % li['uri'])
99
    return li
107
    return li
100
108
109
def cmpentries(e1, e2):
110
    tp1 = e1['tp']
111
    tp2 = e2['tp']
112
    isct1 = tp1 == 'ct'
113
    isct2 = tp2 == 'ct'
114
115
    # Containers come before items, and are sorted in alphabetic order
116
    if isct1 and  not isct2:
117
        return 1
118
    elif not isct1 and isct2:
119
        return -1
120
    elif isct1 and isct2:
121
        tt1 = e1['tt']
122
        tt2 = e2['tt']
123
        if tt1 < tt2:
124
            return -1
125
        elif tt1 > tt2:
126
            return 1
127
        else:
128
            return 0
129
130
    # Tracks. Sort by album then directory then track number
131
    k = 'upnp:album'
132
    a1 = e1[k] if k in e1 else ""
133
    a2 = e2[k] if k in e2 else ""
134
    if a1 < a2:
135
        return -1
136
    elif a1 > a2:
137
        return 1
138
139
    d1 = os.path.dirname(e1['uri'])
140
    d2 = os.path.dirname(e2['uri'])
141
    if d1 < d2:
142
        return -1
143
    elif d1 > d2:
144
        return 1
145
    
146
    k = 'upnp:originalTrackNumber'
147
    a1 = e1[k] if k in e1 else "0"
148
    a2 = e2[k] if k in e2 else "0"
149
    return int(a1) - int(a2)
150
151
101
def rclpathtoreal(path, pathprefix, httphp, pathmap):
152
def rclpathtoreal(path, pathprefix, httphp, pathmap):
102
    path = path.replace(pathprefix, '', 1)
153
    path = path.replace(pathprefix, '', 1)
103
    found = False
154
    found = False
104
    for fsp,htp in pathmap.iteritems():
155
    for fsp,htp in pathmap.iteritems():
105
        if path.startswith(fsp):
156
        if path.startswith(fsp):
...
...
107
            found = True
158
            found = True
108
    if not found:
159
    if not found:
109
        return None
160
        return None
110
    return "http://" + httphp + path
161
    return "http://" + httphp + path
111
162
112
def rcldirentry(id, pid, title, arturi=None, artist=None, upnpclass=None):
163
def rcldirentry(id, pid, title, arturi=None, artist=None, upnpclass=None,
164
                searchable='1'):
113
    """ Create container entry in format expected by parent """
165
    """ Create container entry in format expected by parent """
114
    ret = {'id':id, 'pid':pid, 'tt':title, 'tp':'ct', 'searchable':'1'}
166
    ret = {'id':id, 'pid':pid, 'tt':title, 'tp':'ct', 'searchable':searchable}
115
    if arturi:
167
    if arturi:
116
        ret['upnp:albumArtURI'] = arturi
168
        ret['upnp:albumArtURI'] = arturi
117
    if artist:
169
    if artist:
118
        ret['upnp:artist'] = artist
170
        ret['upnp:artist'] = artist
119
    if upnpclass:
171
    if upnpclass:
...
...
122
        ret['upnp:class'] = 'object.container'
174
        ret['upnp:class'] = 'object.container'
123
    return ret
175
    return ret
124
176
125
def uplog(s):
177
def uplog(s):
126
    print(("%s: %s" % ('uprcl', s)).encode('utf-8'), file=sys.stderr)
178
    print(("%s: %s" % ('uprcl', s)).encode('utf-8'), file=sys.stderr)
179
180
181
# Parse string into (possibly multiword) tokens
182
# 'a b "one phrase" c' -> [a, b, 'one phrase', c]
183
def stringToStrings(str):
184
    # States. Note that ESCAPE can only occur inside INQUOTE
185
    SPACE, TOKEN, INQUOTE, ESCAPE = range(4)
186
    
187
    tokens = []
188
    curtok = ""
189
    state = SPACE;
190
191
    for c in str:
192
        if c == '"':
193
            if state == SPACE:
194
                state = INQUOTE
195
            elif state == TOKEN:
196
                curtok += '"'
197
            elif state == INQUOTE:
198
                if curtok:
199
                    tokens.append(curtok);
200
                curtok = ""
201
                state = SPACE
202
            elif state == ESCAPE:
203
                curtok += '"'
204
                state = INQUOTE
205
            continue;
206
207
        elif c == '\\':
208
            if state == SPACE or state == TOKEN:
209
                curtok += '\\'
210
                state = TOKEN
211
            elif state == INQUOTE:
212
                state = ESCAPE
213
            elif state == ESCAPE:
214
                curtok += '\\'
215
                state = INQUOTE
216
            continue
217
218
        elif c == ' ' or c == '\t' or c == '\n' or c == '\r':
219
            if state == SPACE or state == TOKEN:
220
                if curtok:
221
                    tokens.append(curtok)
222
                curtok = ""
223
                state = SPACE
224
            elif state == INQUOTE or state == ESCAPE:
225
                curtok += c
226
            continue;
227
228
        else:
229
            if state == ESCAPE:
230
                state = INQUOTE
231
            elif state == SPACE:
232
                state = TOKEN
233
            elif state == TOKEN or state == INQUOTE:
234
                pass
235
            curtok += c
236
237
    if state == SPACE:
238
        pass
239
    elif state == TOKEN:
240
        if curtok:
241
            tokens.append(curtok)
242
    elif state == INQUOTE or state == ESCAPE:
243
        raise Exception("Bad string: <" + str + ">")
244
245
    return tokens