Switch to unified view

a/src/mediaserver/cdplugins/uprcl/uprcltags.py b/src/mediaserver/cdplugins/uprcl/uprcltags.py
...
...
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
16
17
import sys
17
import sys
18
import os
18
import os
19
import sqlite3
19
import sqlite3
20
import time
21
import tempfile
20
from timeit import default_timer as timer
22
from timeit import default_timer as timer
21
23
22
from uprclutils import g_myprefix, audiomtypes, docfolder, uplog, \
24
from uprclutils import g_myprefix, audiomtypes, docfolder, uplog, \
23
     rcldirentry, rcldoctoentry, cmpentries
25
     rcldirentry, rcldoctoentry, cmpentries
24
26
25
# After initialization, this holds the list of all records out of
27
# After initialization, this holds the list of all records out of
26
# recoll (it's a reference to the original in uprclfolders)
28
# recoll (it's a reference to the original in uprclfolders)
27
g_alldocs = []
29
g_alldocs = []
30
sqconn = None
28
31
29
sqconn = sqlite3.connect(':memory:')
32
def _sqconn():
33
    # We use a separate thread for building the db to ensure
34
    # responsiveness during this phase.  :memory: handles normally
35
    # can't be shared between threads, and different :memory: handles
36
    # access different dbs. The following would work, but it needs
37
    # python 3.4
38
    #sqconn = sqlite3.connect('file:uprcl_db?mode=memory&cache=shared')
39
    # As we can guarantee that 2 threads will never access the db at
40
    # the same time (the init thread just goes away when it's done),
41
    # we just disable the same_thread checking on :memory:
42
    global sqconn
43
    if sqconn is None:
44
        sqconn = sqlite3.connect(':memory:', check_same_thread=False)
30
#sqconn = sqlite3.connect('/tmp/tracks.sqlite')
45
        #sqconn = sqlite3.connect('/tmp/tracks.sqlite')
46
    return sqconn
31
47
32
# Tags for which we create auxiliary tables for facet descent.
48
# Tags for which we create auxiliary tables for facet descent.
33
#
49
#
34
# TBD: The list will come from the config file one day
50
# TBD: The list will come from the config file one day
35
# TBD: alias et al configuration
51
# TBD: alias et al configuration
...
...
108
# Create the db and fill it up with the values we need, taken out of
124
# Create the db and fill it up with the values we need, taken out of
109
# the recoll records list
125
# the recoll records list
110
def recolltosql(docs):
126
def recolltosql(docs):
111
    global g_alldocs
127
    global g_alldocs
112
    g_alldocs = docs
128
    g_alldocs = docs
113
    
129
    start = timer()
130
131
    sqconn = _sqconn()
114
    _createsqdb(sqconn)
132
    _createsqdb(sqconn)
115
133
116
    # Compute a list of table names and corresponding recoll
134
    # Compute a list of table names and corresponding recoll
117
    # fields. most often they are identical
135
    # fields. most often they are identical
118
    tabfields = []
136
    tabfields = []
...
...
128
    totcnt = 0
146
    totcnt = 0
129
    for docidx in range(len(docs)):
147
    for docidx in range(len(docs)):
130
        doc = docs[docidx]
148
        doc = docs[docidx]
131
        totcnt += 1
149
        totcnt += 1
132
150
151
        if totcnt % 1000 == 0:
152
            time.sleep(0)
153
            
133
        # No need to include non-audio types in the visible tree.
154
        # No need to include non-audio types in the visible tree.
134
        if doc.mtype not in audiomtypes or doc.mtype == 'inode/directory':
155
        if doc.mtype not in audiomtypes or doc.mtype == 'inode/directory':
135
            continue
156
            continue
136
157
137
        # Create album record if needed.
158
        # Create album record if needed.
...
...
193
                c.execute(stmt, (artist_id, album_id))
214
                c.execute(stmt, (artist_id, album_id))
194
            except:
215
            except:
195
                pass
216
                pass
196
                      
217
                      
197
    sqconn.commit()
218
    sqconn.commit()
219
    end = timer()
198
    uplog("recolltosql: processed %d docs" % totcnt)
220
    uplog("recolltosql: processed %d docs in %.2f Seconds" %
221
          (totcnt, end-start))
199
222
200
223
201
# Create our top-level directories, with fixed entries, and stuff from
224
# Create our top-level directories, with fixed entries, and stuff from
202
# the tags tables
225
# the tags tables
203
def rootentries(pid):
226
def rootentries(pid):
204
    c = sqconn.cursor()
227
    c = _sqconn().cursor()
205
    c.execute("SELECT COUNT(*) from albums")
228
    c.execute("SELECT COUNT(*) from albums")
206
    nalbs = str(c.fetchone()[0])
229
    nalbs = str(c.fetchone()[0])
207
    c.execute("SELECT COUNT(*) from tracks")
230
    c.execute("SELECT COUNT(*) from tracks")
208
    nitems = str(c.fetchone()[0])
231
    nitems = str(c.fetchone()[0])
209
    entries = [rcldirentry(pid + 'albums', pid, nalbs + ' albums'),
232
    entries = [rcldirentry(pid + 'albums', pid, nalbs + ' albums'),
...
...
215
# Check what tags still have multiple values inside the selected set,
238
# Check what tags still have multiple values inside the selected set,
216
# and return their list.
239
# and return their list.
217
def _subtreetags(docidsl):
240
def _subtreetags(docidsl):
218
    docids = ','.join([str(i) for i in docidsl])
241
    docids = ','.join([str(i) for i in docidsl])
219
    uplog("subtreetags, docids %s" % docids)
242
    uplog("subtreetags, docids %s" % docids)
220
    c = sqconn.cursor()
243
    c = _sqconn().cursor()
221
    tags = []
244
    tags = []
222
    for tt,tb in tagtables.iteritems():
245
    for tt,tb in tagtables.iteritems():
223
        stmt = 'SELECT COUNT(DISTINCT ' + _clid(tb) + \
246
        stmt = 'SELECT COUNT(DISTINCT ' + _clid(tb) + \
224
               ') FROM tracks WHERE docidx IN (' + docids + ')'
247
               ') FROM tracks WHERE docidx IN (' + docids + ')'
225
        uplog("subtreetags: executing: <%s>" % stmt)
248
        uplog("subtreetags: executing: <%s>" % stmt)
...
...
230
            if cnt > 1:
253
            if cnt > 1:
231
                tags.append(tt)
254
                tags.append(tt)
232
    return tags
255
    return tags
233
256
234
def _trackentriesforstmt(stmt, values, pid, httphp, pathprefix):
257
def _trackentriesforstmt(stmt, values, pid, httphp, pathprefix):
235
    c = sqconn.cursor()
258
    c = _sqconn().cursor()
236
    c.execute(stmt, values)
259
    c.execute(stmt, values)
237
    entries = []
260
    entries = []
238
    for r in c:
261
    for r in c:
239
        docidx = r[0]
262
        docidx = r[0]
240
        id = pid + '$i' + str(docidx)
263
        id = pid + '$i' + str(docidx)
...
...
245
268
246
# Return a list of trackids as selected by the current
269
# Return a list of trackids as selected by the current
247
# path <selwhere> is like: WHERE col1_id = ? AND col2_id = ? [...], and
270
# path <selwhere> is like: WHERE col1_id = ? AND col2_id = ? [...], and
248
# <values> holds the corresponding values
271
# <values> holds the corresponding values
249
def _docidsforsel(selwhere, values):
272
def _docidsforsel(selwhere, values):
250
    c = sqconn.cursor()
273
    c = _sqconn().cursor()
251
    stmt = 'SELECT docidx FROM tracks ' + selwhere + ' ORDER BY trackno'
274
    stmt = 'SELECT docidx FROM tracks ' + selwhere + ' ORDER BY trackno'
252
    uplog("docidsforsel: executing <%s> values %s" % (stmt, values))
275
    uplog("docidsforsel: executing <%s> values %s" % (stmt, values))
253
    c.execute(stmt, values)
276
    c.execute(stmt, values)
254
    return [r[0] for r in c.fetchall()]
277
    return [r[0] for r in c.fetchall()]
255
278
...
...
257
    stmt = 'SELECT docidx FROM tracks WHERE album_id = ? ORDER BY trackno'
280
    stmt = 'SELECT docidx FROM tracks WHERE album_id = ? ORDER BY trackno'
258
    return _trackentriesforstmt(stmt, (albid,), pid, httphp, pathprefix)
281
    return _trackentriesforstmt(stmt, (albid,), pid, httphp, pathprefix)
259
282
260
def _direntriesforalbums(pid, where):
283
def _direntriesforalbums(pid, where):
261
    entries = []
284
    entries = []
262
    c = sqconn.cursor()
285
    c = _sqconn().cursor()
263
    if not where:
286
    if not where:
264
        where = 'WHERE artist.artist_id = albums.artist_id'
287
        where = 'WHERE artist.artist_id = albums.artist_id'
265
    else:
288
    else:
266
        where += ' AND artist.artist_id = albums.artist_id'
289
        where += ' AND artist.artist_id = albums.artist_id'
267
290
...
...
275
    return entries
298
    return entries
276
299
277
# This is called when an 'albums' element is encountered in the
300
# This is called when an 'albums' element is encountered in the
278
# selection path.
301
# selection path.
279
def _tagsbrowsealbums(pid, qpath, i, selwhere, values, httphp, pathprefix):
302
def _tagsbrowsealbums(pid, qpath, i, selwhere, values, httphp, pathprefix):
280
    c = sqconn.cursor()
303
    c = _sqconn().cursor()
281
    docidsl = _docidsforsel(selwhere, values)
304
    docidsl = _docidsforsel(selwhere, values)
282
    entries = []
305
    entries = []
283
    if i == len(qpath)-1:
306
    if i == len(qpath)-1:
284
        albidsl = _subtreealbums(docidsl)
307
        albidsl = _subtreealbums(docidsl)
285
        albids = ','.join([str(a) for a in albidsl])
308
        albids = ','.join([str(a) for a in albidsl])
...
...
323
def _subtreealbums(docidsl):
346
def _subtreealbums(docidsl):
324
    docids = ','.join([str(r) for r in docidsl])
347
    docids = ','.join([str(r) for r in docidsl])
325
    albids = []
348
    albids = []
326
    stmt = 'SELECT album_id from tracks where docidx IN (' + docids + ') ' + \
349
    stmt = 'SELECT album_id from tracks where docidx IN (' + docids + ') ' + \
327
           'GROUP BY album_id'
350
           'GROUP BY album_id'
328
    c = sqconn.cursor()
351
    c = _sqconn().cursor()
329
    uplog('subtreealbums: executing %s' % stmt)
352
    uplog('subtreealbums: executing %s' % stmt)
330
    c.execute(stmt)
353
    c.execute(stmt)
331
    for r in c:
354
    for r in c:
332
        albids.append(r[0])
355
        albids.append(r[0])
333
    uplog('subtreealbums: returning %s' % albids)
356
    uplog('subtreealbums: returning %s' % albids)
...
...
415
        stmt = "SELECT " + selwhat + " FROM tracks, " + col + \
438
        stmt = "SELECT " + selwhat + " FROM tracks, " + col + \
416
               selwhere + \
439
               selwhere + \
417
               " GROUP BY tracks." + _clid(col) + \
440
               " GROUP BY tracks." + _clid(col) + \
418
               " ORDER BY value"
441
               " ORDER BY value"
419
        uplog("tagsbrowse: executing <%s> values %s" % (stmt,values))
442
        uplog("tagsbrowse: executing <%s> values %s" % (stmt,values))
420
        c = sqconn.cursor()
443
        c = _sqconn().cursor()
421
        c.execute(stmt, values)
444
        c.execute(stmt, values)
422
        for r in c:
445
        for r in c:
423
            id = pid + '$' + str(r[0])
446
            id = pid + '$' + str(r[0])
424
            entries.append(rcldirentry(id, pid, r[1]))
447
            entries.append(rcldirentry(id, pid, r[1]))
425
    return entries
448
    return entries
...
...
427
450
428
# Browse the top-level tree named like 'xxx albums'. There are just 2
451
# Browse the top-level tree named like 'xxx albums'. There are just 2
429
# levels: the whole albums list, then for each entry the specified
452
# levels: the whole albums list, then for each entry the specified
430
# albums track list
453
# albums track list
431
def _albumsbrowse(pid, qpath, flag, httphp, pathprefix):
454
def _albumsbrowse(pid, qpath, flag, httphp, pathprefix):
432
    c = sqconn.cursor()
455
    c = _sqconn().cursor()
433
    entries = []
456
    entries = []
434
    if len(qpath) == 1:
457
    if len(qpath) == 1:
435
        entries = _direntriesforalbums(pid, '')
458
        entries = _direntriesforalbums(pid, '')
436
    elif len(qpath) == 2:
459
    elif len(qpath) == 2:
437
        e1 = qpath[1]
460
        e1 = qpath[1]
...
...
470
493
471
494
472
############ Misc test/trial code, not used by uprcl ########################
495
############ Misc test/trial code, not used by uprcl ########################
473
496
474
def misctries():
497
def misctries():
475
    c = sqconn.cursor()
498
    c = _sqconn().cursor()
476
    c.execute('''SELECT COUNT(*) FROM tracks''')
499
    c.execute('''SELECT COUNT(*) FROM tracks''')
477
    uplog("Count(*) %d" % (c.fetchone()[0],))
500
    uplog("Count(*) %d" % (c.fetchone()[0],))
478
    
501
    
479
    #for row in c.execute('''SELECT album
502
    #for row in c.execute('''SELECT album
480
    #                        FROM tracks where artist LIKE "%Gould%"
503
    #                        FROM tracks where artist LIKE "%Gould%"