1
|
|
|
""" |
2
|
|
|
SQLite plugin object. |
3
|
|
|
This plugin stores metadata about all media in an sqlite database. |
4
|
|
|
|
5
|
|
|
You'll need to include [PluginSQLite] in your config file. |
6
|
|
|
By default, the sqlite database will be created in your application |
7
|
|
|
directory. If you want to specify a different path then |
8
|
|
|
specify a fully qualified `database_file` path. |
9
|
|
|
|
10
|
|
|
``` |
11
|
|
|
[PluginSQLite] |
12
|
|
|
database_file=/path/to/database.db |
13
|
|
|
|
14
|
|
|
.. moduleauthor:: Jaisen Mathai <[email protected]> |
15
|
|
|
""" |
16
|
|
|
from __future__ import print_function |
17
|
|
|
|
18
|
|
|
import json |
19
|
|
|
import os |
20
|
|
|
import sqlite3 |
21
|
|
|
import time |
22
|
|
|
#import json |
23
|
|
|
|
24
|
|
|
#from google_auth_oauthlib.flow import InstalledAppFlow |
25
|
|
|
#from google.auth.transport.requests import AuthorizedSession |
26
|
|
|
#from google.oauth2.credentials import Credentials |
27
|
|
|
|
28
|
|
|
from elodie.geolocation import place_name |
29
|
|
|
from elodie.localstorage import Db |
30
|
|
|
from elodie.media.base import Base, get_all_subclasses |
31
|
|
|
from elodie.plugins.plugins import PluginBase |
32
|
|
|
|
33
|
|
|
class SQLite(PluginBase): |
34
|
|
|
"""A class to execute plugin actions. |
35
|
|
|
|
36
|
|
|
Requires a config file with the following configurations set. |
37
|
|
|
database_file: |
38
|
|
|
The full path to the SQLite database (.db). |
39
|
|
|
|
40
|
|
|
""" |
41
|
|
|
|
42
|
|
|
__name__ = 'SQLite' |
43
|
|
|
|
44
|
|
|
def __init__(self): |
45
|
|
|
super(SQLite, self).__init__() |
46
|
|
|
|
47
|
|
|
# Default the database file to be in the application plugin directory. |
48
|
|
|
# Override with value from config file. |
49
|
|
|
self.database_file = '{}/plugins/{}/elodie.db'.format( |
50
|
|
|
self.application_directory, |
51
|
|
|
__name__.lower() |
52
|
|
|
) |
53
|
|
|
if('database_file' in self.config_for_plugin): |
54
|
|
|
self.database_file = self.config_for_plugin['database_file'] |
55
|
|
|
|
56
|
|
|
self.con = sqlite3.connect(self.database_file) |
57
|
|
|
self.con.row_factory = sqlite3.Row |
58
|
|
|
self.cursor = self.con.cursor() |
59
|
|
|
if(not self._validate_schema()): |
60
|
|
|
self._create_schema() |
61
|
|
|
|
62
|
|
|
def after(self, file_path, destination_folder, final_file_path, metadata): |
63
|
|
|
self._upsert(file_path, destination_folder, final_file_path, metadata) |
64
|
|
|
|
65
|
|
|
def batch(self): |
66
|
|
|
pass |
67
|
|
|
|
68
|
|
|
def before(self, file_path, destination_folder): |
69
|
|
|
pass |
70
|
|
|
|
71
|
|
|
def generate_db(self): |
72
|
|
|
db = Db() |
73
|
|
|
for checksum, file_path in db.all(): |
74
|
|
|
subclasses = get_all_subclasses() |
75
|
|
|
media = Base.get_class_by_file(file_path, get_all_subclasses()) |
76
|
|
|
media.set_checksum( |
77
|
|
|
db.checksum(file_path) |
78
|
|
|
) |
79
|
|
|
metadata = media.get_metadata() |
80
|
|
|
destination_folder = os.path.dirname(file_path) |
81
|
|
|
final_file_path = '{}{}'.format(os.path.sep, os.path.basename(file_path)) |
82
|
|
|
self._upsert(file_path, destination_folder, final_file_path, metadata) |
83
|
|
|
|
84
|
|
|
def _create_schema(self): |
85
|
|
|
self.database_schema = '{}{}{}'.format( |
86
|
|
|
os.path.dirname(os.path.realpath(__file__)), |
87
|
|
|
os.sep, |
88
|
|
|
'schema.sql' |
89
|
|
|
) |
90
|
|
|
|
91
|
|
|
with open(self.database_schema, 'r') as fp_schema: |
92
|
|
|
sql_statement = fp_schema.read() |
93
|
|
|
self.cursor.executescript(sql_statement) |
94
|
|
|
|
95
|
|
|
def _insert_row_sql(self, final_path, metadata): |
96
|
|
|
path = '{}/{}.{}'.format( |
97
|
|
|
metadata['directory_path'], |
98
|
|
|
metadata['base_name'], |
99
|
|
|
metadata['extension'] |
100
|
|
|
) |
101
|
|
|
return ( |
102
|
|
|
"""INSERT INTO `metadata` ( |
103
|
|
|
`hash`, `path`, `album`, `camera_make`, `camera_model`, |
104
|
|
|
`date_taken`, `latitude`, `location_name`, `longitude`, |
105
|
|
|
`original_name`, `title`, `_modified`) |
106
|
|
|
VALUES ( |
107
|
|
|
:hash, :path, :album, :camera_make, :camera_model, |
108
|
|
|
:date_taken, :latitude, :location_name, :longitude, |
109
|
|
|
:original_name, :title, datetime('now'))""", |
110
|
|
|
self._sql_values(final_path, metadata) |
111
|
|
|
) |
112
|
|
|
|
113
|
|
|
def _run_query(self, sql, values): |
114
|
|
|
self.cursor.execute(sql, values) |
115
|
|
|
return self.cursor.fetchall() |
116
|
|
|
|
117
|
|
|
def _sql_values(self, final_path, metadata, current_path=None): |
118
|
|
|
timestamp = int(time.time()) |
119
|
|
|
return { |
120
|
|
|
'hash': metadata['checksum'], |
121
|
|
|
'path': final_path, |
122
|
|
|
'album': metadata['album'], |
123
|
|
|
'camera_make': metadata['camera_make'], |
124
|
|
|
'camera_model': metadata['camera_model'], |
125
|
|
|
'date_taken': time.strftime('%Y-%m-%d %H:%M:%S', metadata['date_taken']), |
126
|
|
|
'latitude': metadata['latitude'], |
127
|
|
|
'location_name': place_name(metadata['latitude'], metadata['longitude'])['default'], |
128
|
|
|
'longitude': metadata['longitude'], |
129
|
|
|
'original_name': metadata['original_name'], |
130
|
|
|
'title': metadata['title'], |
131
|
|
|
'current_path': current_path |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
def _update_row_sql(self, current_path, final_path, metadata): |
135
|
|
|
timestamp = int(time.time()) |
136
|
|
|
return ( |
137
|
|
|
"""UPDATE `metadata` SET `hash`=:hash, `path`=:path, `album`=:album, `camera_make`=:camera_make, |
138
|
|
|
`camera_model`=:camera_model, `date_taken`=:date_taken, `latitude`=:latitude, |
139
|
|
|
`longitude`=:longitude, `original_name`=:original_name, `title`=:title, `_modified`=datetime('now') |
140
|
|
|
WHERE `path`=:current_path""", |
141
|
|
|
self._sql_values(final_path, metadata, current_path) |
142
|
|
|
) |
143
|
|
|
|
144
|
|
|
def _upsert(self, file_path, destination_folder, final_file_path, metadata): |
145
|
|
|
# We check if the source path exists in the database already. |
146
|
|
|
# If it does then we assume that this is an update operation. |
147
|
|
|
full_destination_path = '{}{}'.format(destination_folder, final_file_path) |
148
|
|
|
self.cursor.execute("SELECT `path` FROM `metadata` WHERE `path`=:path", {'path': file_path}) |
149
|
|
|
|
150
|
|
|
if(self.cursor.fetchone() is None): |
151
|
|
|
self.log(u'SQLite plugin inserting {}'.format(file_path)) |
152
|
|
|
sql_statement, sql_values = self._insert_row_sql(full_destination_path, metadata) |
153
|
|
|
else: |
154
|
|
|
self.log(u'SQLite plugin updating {}'.format(file_path)) |
155
|
|
|
sql_statement, sql_values = self._update_row_sql(file_path, full_destination_path, metadata) |
156
|
|
|
|
157
|
|
|
self.cursor.execute(sql_statement, sql_values) |
158
|
|
|
|
159
|
|
|
def _validate_schema(self): |
160
|
|
|
try: |
161
|
|
|
self.cursor.execute('SELECT * FROM `metadata` LIMIT 1'); |
162
|
|
|
return True |
163
|
|
|
except sqlite3.OperationalError: |
164
|
|
|
return False |
165
|
|
|
|