3 # This file is part of Panucci.
4 # Copyright (c) 2008-2009 The Panucci Audiobook and Podcast Player Project
6 # Panucci is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # Panucci is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with Panucci. If not, see <http://www.gnu.org/licenses/>.
19 # _Heavily_ inspired by gPodder's dbsqlite
27 log
= logging
.getLogger('panucci.dbsqlite')
30 from sqlite3
import dbapi2
as sqlite
32 log
.critical('Error importing sqlite, FAIL!')
34 from settings
import settings
35 from simplegconf
import gconf
37 class Storage(object):
38 def __init__(self
, db_file
):
39 """ db_file is the on-disk location of the database file """
40 self
.__db
_file
= db_file
42 self
.__log
= logging
.getLogger('panucci.dbsqlite.Storage')
47 self
.__db
= sqlite
.connect(self
.__db
_file
)
48 self
.__log
.debug('Connected to %s', self
.__db
_file
)
53 return self
.db
.cursor()
57 self
.__log
.debug('COMMIT')
59 except ProgrammingError
, e
:
60 self
.__log
.error('Error commiting changes!', exc_info
=1)
62 def __check_schema(self
):
64 Creates all necessary tables and indexes that don't exist.
66 cursor
= self
.cursor()
69 """ CREATE TABLE IF NOT EXISTS bookmarks (
70 bookmark_id INTEGER PRIMARY KEY AUTOINCREMENT,
73 bookmark_filepath TEXT,
74 seek_position INTEGER,
76 is_resume_position INTEGER,
77 playlist_duplicate_id INTEGER
81 """ CREATE TABLE IF NOT EXISTS playlists (
82 playlist_id INTEGER PRIMARY KEY AUTOINCREMENT,
90 #################################
91 # Bookmark-related functions
93 def get_bookmarks(self
, playlist_id
=None, bookmark_filepath
=None,
94 playlist_duplicate_id
=None, request_resume_bookmark
=None):
95 """ Note: the request_resume_bookmark argument will only return resume
96 bookmarks if it's set to True. If you don't care whether you're
97 getting resume bookmarks or not set it to None """
99 sql
= 'SELECT * FROM bookmarks'
103 if playlist_id
is not None:
104 conditions
.append('playlist_id = ?')
105 args
.append(playlist_id
)
107 if bookmark_filepath
is not None:
108 conditions
.append('bookmark_filepath = ?')
109 args
.append(bookmark_filepath
)
111 if playlist_duplicate_id
is not None:
112 conditions
.append('playlist_duplicate_id = ?')
113 args
.append(playlist_duplicate_id
)
115 if request_resume_bookmark
is not None:
116 conditions
.append('is_resume_position = ?')
117 args
.append(request_resume_bookmark
)
122 sql
+= string
.join(conditions
, ' AND ')
124 cursor
= self
.cursor()
125 cursor
.execute( sql
, args
)
126 bookmarks
= cursor
.fetchall()
131 def bookmark_exists(self
, playlist_id
):
132 return self
.get_bookmarks( playlist_id
) is not None
134 def load_bookmarks(self
, factory
, *args
, **kwargs
):
135 """ Load bookmarks into a dict and return a list of dicts or
136 return a list of the outputs from the factory function.
137 Set the factory function to None to not use it.
138 Note: This is a wrapper around get_bookmarks, see get_bookmarks
139 for all available arguments. """
141 bkmks
= self
.get_bookmarks( *args
, **kwargs
)
150 'bookmark_name' : bkmk
[1],
151 'playlist_id' : bkmk
[2],
152 'bookmark_filepath' : bkmk
[3],
153 'seek_position' : bkmk
[4],
154 'timestamp' : bkmk
[5],
155 'is_resume_position' : bool(bkmk
[6]),
156 'playlist_duplicate_id' : bkmk
[7],
159 if factory
is not None:
162 bkmk_list
.append(BKMK
)
166 def save_bookmark(self
, bookmark
):
168 self
.__log
.warn('Not saving bookmark with negative id (%d)', bookmark
.id)
171 if bookmark
.playlist_id
is None:
172 self
.__log
.warn('Not saving bookmark without playlist filepath')
175 if bookmark
.is_resume_position
:
176 self
.remove_resume_bookmark( bookmark
.playlist_id
)
178 self
.__log
.info('Saving %s, %d (%s)', bookmark
.bookmark_name
,
179 bookmark
.seek_position
, bookmark
.bookmark_filepath
)
181 cursor
= self
.cursor()
183 """ INSERT INTO bookmarks (
190 playlist_duplicate_id
191 ) VALUES (?, ?, ?, ?, ?, ?, ?) """,
193 ( bookmark
.bookmark_name
, bookmark
.playlist_id
,
194 bookmark
.bookmark_filepath
, bookmark
.seek_position
,
195 bookmark
.timestamp
, bookmark
.is_resume_position
,
196 bookmark
.playlist_duplicate_id
))
198 r_id
= self
.__get
__( 'SELECT last_insert_rowid()' )
205 def update_bookmark(self
, bookmark
):
206 self
.__log
.info('Updating %s (%s)',
207 bookmark
.bookmark_name
, bookmark
.playlist_id
)
209 cursor
= self
.cursor()
211 """ UPDATE bookmarks SET
214 bookmark_filepath = ?,
217 is_resume_position = ?,
218 playlist_duplicate_id = ?
219 WHERE bookmark_id = ? """,
221 ( bookmark
.bookmark_name
, bookmark
.playlist_id
,
222 bookmark
.bookmark_filepath
, bookmark
.seek_position
,
223 bookmark
.timestamp
, bookmark
.is_resume_position
,
224 bookmark
.playlist_duplicate_id
, bookmark
.id ))
229 def remove_bookmark(self
, bookmark_id
):
230 self
.__log
.info('Deleting bookmark by id: %s', bookmark_id
)
231 assert bookmark_id
>= 0
233 cursor
= self
.cursor()
235 'DELETE FROM bookmarks WHERE bookmark_id = ?', (bookmark_id
,) )
240 def remove_resume_bookmark(self
, playlist_id
):
241 self
.__log
.info('Deleting resume bookmark for: %s', playlist_id
)
243 cursor
= self
.cursor()
245 """ DELETE FROM bookmarks WHERE
247 is_resume_position = 1 """,
254 def remove_all_bookmarks(self
, playlist_id
):
255 self
.__log
.info('Deleting all bookmarks for: %s', playlist_id
)
257 cursor
= self
.cursor()
259 """ DELETE FROM bookmarks WHERE
268 #################################
269 # Playlist-related functions
271 def playlist_exists(self
, filepath
):
272 if filepath
is not None and filepath
:
273 return self
.__get
__( 'SELECT * FROM playlists WHERE filepath = ?',
274 filepath
) is not None
278 def get_playlist_id(self
, filepath
, create_new
=False, update_time
=False):
279 """ Get a playlist_id by it's filepath
280 create_new: if True it will create a new playlist
281 entry if none exists for the filepath.
282 update_time: if True it updates the timestamp for the
283 playlist entry of the filepath. """
285 if self
.playlist_exists(filepath
):
286 playlist_id
= self
.__get
__(
287 'SELECT playlist_id FROM playlists WHERE filepath = ?',
290 playlist_id
= self
.add_playlist( filepath
)
294 if playlist_id
is not None and update_time
:
295 self
.update_playlist( playlist_id
, filepath
)
299 def add_playlist(self
, filepath
, timestamp
=None):
300 self
.__log
.info( 'Adding playlist: %s', filepath
)
302 if timestamp
is None:
303 timestamp
= time
.time()
305 cursor
= self
.cursor()
307 """ INSERT INTO playlists (filepath, timestamp) VALUES (?,?) """,
308 ( filepath
, timestamp
) )
313 r_id
= self
.__get
__( 'SELECT last_insert_rowid()' )[0]
317 def update_playlist(self
, playlist_id
, filepath
, timestamp
=None):
318 self
.__log
.info( 'Updating playlist: %s', filepath
)
320 if timestamp
is None:
321 timestamp
= time
.time()
323 cursor
= self
.cursor()
325 """ UPDATE playlists SET
328 WHERE playlist_id = ? """,
330 ( filepath
, timestamp
, playlist_id
) )
335 def delete_playlist(self
, playlist_id
):
336 self
.__log
.info( 'Deleting playlist: %d', playlist_id
)
338 cursor
= self
.cursor()
340 """ DELETE FROM playlists WHERE playlist_id = ? """,
346 def get_latest_files(self
):
347 self
.__log
.debug('Finding latest files...')
348 cursor
= self
.cursor()
351 """ SELECT filepath FROM playlists
352 ORDER BY timestamp DESC """)
354 files
= cursor
.fetchall()
357 return [ f
[0] for f
in files
]
360 def __get__(self
, sql
, params
=None):
361 """ Returns the first row of a query result """
363 cursor
= self
.cursor()
368 if not isinstance( params
, (list, tuple) ):
371 cursor
.execute(sql
, params
)
373 row
= cursor
.fetchone()
378 db
= Storage(os
.path
.expanduser(settings
.db_location
))