1# Copyright (c) 2014-2021 Cedric Bellegarde <cedric.bellegarde@adishatz.org> 2# This program is free software: you can redistribute it and/or modify 3# it under the terms of the GNU General Public License as published by 4# the Free Software Foundation, either version 3 of the License, or 5# (at your option) any later version. 6# This program is distributed in the hope that it will be useful, 7# but WITHOUT ANY WARRANTY; without even the implied warranty of 8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9# GNU General Public License for more details. 10# You should have received a copy of the GNU General Public License 11# along with this program. If not, see <http://www.gnu.org/licenses/>. 12 13from gi.repository import Gio 14 15import sqlite3 16from threading import Lock 17 18from lollypop.define import CACHE_PATH 19from lollypop.sqlcursor import SqlCursor 20from lollypop.database import Database 21from lollypop.logger import Logger 22 23 24class CacheDatabase: 25 """ 26 Cache calculation into database 27 """ 28 DB_PATH = "%s/cache_v1.db" % CACHE_PATH 29 30 # SQLite documentation: 31 # In SQLite, a column with type INTEGER PRIMARY KEY 32 # is an alias for the ROWID. 33 # Here, we define an id INT PRIMARY KEY but never feed it, 34 # this make VACUUM not destroy rowids... 35 __create_duration = """CREATE TABLE duration ( 36 id TEXT PRIMARY KEY, 37 album_id INT NOT NULL, 38 duration INT NOT NULL DEFAULT 0)""" 39 40 def __init__(self): 41 """ 42 Create database tables 43 """ 44 self.thread_lock = Lock() 45 f = Gio.File.new_for_path(self.DB_PATH) 46 if not f.query_exists(): 47 try: 48 d = Gio.File.new_for_path(CACHE_PATH) 49 if not d.query_exists(): 50 d.make_directory_with_parents() 51 # Create db schema 52 with SqlCursor(self, True) as sql: 53 sql.execute(self.__create_duration) 54 except Exception as e: 55 Logger.error("DatabaseCache::__init__(): %s" % e) 56 57 def set_duration(self, album_id, album_hash, duration): 58 """ 59 Set duration in cache for album 60 @param album_id as int 61 @param album_hash as str 62 @param duration as int 63 """ 64 try: 65 with SqlCursor(self, True) as sql: 66 sql.execute("INSERT INTO duration (id, album_id, duration)\ 67 VALUES (?, ?, ?)", 68 (album_hash, album_id, duration)) 69 except Exception as e: 70 Logger.error("DatabaseCache::set_duration(): %s", e) 71 72 def get_duration(self, album_hash): 73 """ 74 Get duration in cache for album 75 @param album_hash as str 76 @return int/None 77 """ 78 try: 79 with SqlCursor(self) as sql: 80 result = sql.execute("SELECT duration\ 81 FROM duration\ 82 WHERE id=?", 83 (album_hash,)) 84 v = result.fetchone() 85 if v is not None: 86 return v[0] 87 except Exception as e: 88 Logger.error("DatabaseCache::get_duration(): %s", e) 89 return None 90 91 def clear_durations(self, album_id): 92 """ 93 Clear durations for album_id 94 @param album_id as int 95 """ 96 with SqlCursor(self, True) as sql: 97 sql.execute("DELETE FROM duration WHERE album_id=?", 98 (album_id,)) 99 100 def clear_table(self, table): 101 """ 102 Clear table 103 @param table as str 104 """ 105 with SqlCursor(self, True) as sql: 106 sql.execute('DELETE FROM "%s"' % table) 107 108 def clean(self, commit=True): 109 """ 110 Clean cache 111 @param commit as bool 112 """ 113 with SqlCursor(self, commit) as sql: 114 sql.execute('ATTACH DATABASE "%s" AS music' % Database.DB_PATH) 115 sql.execute("DELETE FROM duration WHERE duration.album_id NOT IN (\ 116 SELECT albums.rowid FROM music.albums)") 117 118 def get_cursor(self): 119 """ 120 Return a new sqlite cursor 121 """ 122 try: 123 c = sqlite3.connect(self.DB_PATH, 600.0) 124 return c 125 except: 126 exit(-1) 127 128####################### 129# PRIVATE # 130####################### 131