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 GLib 14 15import sqlite3 16from threading import Lock 17 18from lollypop.sqlcursor import SqlCursor 19 20 21class History: 22 """ 23 History manager, allow Lollypop to restore stats when a file is moved 24 """ 25 __LOCAL_PATH = GLib.get_user_data_dir() + "/lollypop" 26 __DB_PATH = "%s/history.db" % __LOCAL_PATH 27 __LIMIT = 1000000 # Delete when limit is reached 28 __DELETE = 100 # How many elements to delete 29 __create_history = """CREATE TABLE history ( 30 id INTEGER PRIMARY KEY, 31 name TEXT NOT NULL, 32 duration INT NOT NULL, 33 ltime INT NOT NULL, 34 popularity INT NOT NULL, 35 rate INT NOT NULL, 36 mtime INT NOT NULL, 37 album_rate INT NOT NULL, 38 loved INT NOT NULL, 39 album_loved INT NOT NULL, 40 album_synced INT NOT NULL, 41 album_popularity INT NOT NULL)""" 42 43 def __init__(self): 44 """ 45 Init playlists manager 46 """ 47 self.thread_lock = Lock() 48 # Create db schema 49 try: 50 with SqlCursor(self, True) as sql: 51 sql.execute(self.__create_history) 52 except: 53 pass 54 with SqlCursor(self, True) as sql: 55 result = sql.execute("SELECT COUNT(*)\ 56 FROM history") 57 v = result.fetchone() 58 if v is not None and v[0] > self.__LIMIT: 59 sql.execute("DELETE FROM history\ 60 WHERE rowid IN (SELECT rowid\ 61 FROM history\ 62 LIMIT %s)" % self.__DELETE) 63 sql.execute("VACUUM") 64 65 def add(self, name, duration, popularity, rate, ltime, mtime, loved, 66 album_loved, album_popularity, album_rate, album_synced): 67 """ 68 Add an item to history 69 @param name as str 70 @param duration as int 71 @param popularity as int 72 @param rate as int 73 @param ltime as int 74 @param mtime as int 75 @param loved as bool 76 @param album_loved as bool 77 @param album_popularity as int 78 @param album_rate as int 79 @param album_synced as int 80 @thread safe 81 """ 82 with SqlCursor(self, True) as sql: 83 # Needed because of seconds to ms DB migration 84 # Value in DB is rounded version of Gstreamer value 85 duration //= 1000 86 if self.exists(name, duration): 87 sql.execute("UPDATE history\ 88 SET popularity=?,rate=?,ltime=?,mtime=?,loved=?,\ 89 album_loved=?,album_popularity=?,album_rate=?,\ 90 album_synced=?\ 91 WHERE name=? AND duration=?", 92 (popularity, rate, ltime, mtime, loved, 93 album_loved, album_popularity, album_rate, 94 album_synced, 95 name, duration)) 96 else: 97 sql.execute("INSERT INTO history\ 98 (name, duration, popularity, rate, ltime, mtime,\ 99 loved, album_loved, album_popularity, album_rate,\ 100 album_synced)\ 101 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", 102 (name, duration, popularity, rate, ltime, mtime, 103 loved, album_loved, album_popularity, album_rate, 104 album_synced)) 105 106 def get(self, name, duration): 107 """ 108 Get stats for track with name and duration 109 @param name as str 110 @param duration as int 111 @return (popularity, ltime, mtime, 112 loved album, album_popularity, album_synced) 113 as (int, int, int, int, int, int) 114 """ 115 with SqlCursor(self) as sql: 116 duration //= 1000 117 result = sql.execute("SELECT popularity, rate, ltime, mtime,\ 118 loved, album_loved, album_popularity,\ 119 album_rate, album_synced\ 120 FROM history\ 121 WHERE name=?\ 122 AND duration=?", 123 (name, duration)) 124 v = result.fetchone() 125 if v is not None: 126 return v 127 return (0, 0, 0, 0, 0, 0, 0, 0, 0) 128 129 def exists(self, name, duration): 130 """ 131 Return True if entry exists 132 @param name as str 133 @param duration as int 134 @return bool 135 """ 136 with SqlCursor(self) as sql: 137 result = sql.execute("SELECT rowid\ 138 FROM history\ 139 WHERE name=?\ 140 AND duration=?", 141 (name, duration)) 142 v = result.fetchone() 143 if v is not None: 144 return True 145 else: 146 return False 147 148 def get_cursor(self): 149 """ 150 Return a new sqlite cursor 151 """ 152 try: 153 return sqlite3.connect(self.__DB_PATH, 600.0) 154 except: 155 exit(-1) 156 157####################### 158# PRIVATE # 159####################### 160