SqliteDeviceRepository.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.IO;
  5. using System.Linq;
  6. using Emby.Server.Implementations.Data;
  7. using MediaBrowser.Controller;
  8. using MediaBrowser.Model.Logging;
  9. using SQLitePCL.pretty;
  10. using MediaBrowser.Model.Extensions;
  11. using MediaBrowser.Model.IO;
  12. using MediaBrowser.Common.Extensions;
  13. using MediaBrowser.Controller.Devices;
  14. using MediaBrowser.Model.Devices;
  15. using MediaBrowser.Model.Serialization;
  16. using MediaBrowser.Model.Session;
  17. using MediaBrowser.Controller.Configuration;
  18. namespace Emby.Server.Implementations.Devices
  19. {
  20. public class SqliteDeviceRepository : BaseSqliteRepository, IDeviceRepository
  21. {
  22. private readonly CultureInfo _usCulture = new CultureInfo("en-US");
  23. protected IFileSystem FileSystem { get; private set; }
  24. private readonly object _syncLock = new object();
  25. private readonly IJsonSerializer _json;
  26. private IServerApplicationPaths _appPaths;
  27. public SqliteDeviceRepository(ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IJsonSerializer json)
  28. : base(logger)
  29. {
  30. var appPaths = config.ApplicationPaths;
  31. DbFilePath = Path.Combine(appPaths.DataPath, "devices.db");
  32. FileSystem = fileSystem;
  33. _json = json;
  34. _appPaths = appPaths;
  35. }
  36. public void Initialize()
  37. {
  38. try
  39. {
  40. InitializeInternal();
  41. }
  42. catch (Exception ex)
  43. {
  44. Logger.ErrorException("Error loading database file. Will reset and retry.", ex);
  45. FileSystem.DeleteFile(DbFilePath);
  46. InitializeInternal();
  47. }
  48. }
  49. private void InitializeInternal()
  50. {
  51. using (var connection = CreateConnection())
  52. {
  53. RunDefaultInitialization(connection);
  54. string[] queries = {
  55. "create table if not exists Devices (Id TEXT PRIMARY KEY, Name TEXT NOT NULL, ReportedName TEXT NOT NULL, CustomName TEXT, CameraUploadPath TEXT, LastUserName TEXT NOT NULL, AppName TEXT NOT NULL, AppVersion TEXT NOT NULL, LastUserId TEXT NOT NULL, DateLastModified DATETIME NOT NULL, Capabilities TEXT NOT NULL)",
  56. "create index if not exists idx_id on Devices(Id)"
  57. };
  58. connection.RunQueries(queries);
  59. MigrateDevices();
  60. }
  61. }
  62. private void MigrateDevices()
  63. {
  64. List<string> files;
  65. try
  66. {
  67. files = FileSystem
  68. .GetFilePaths(GetDevicesPath(), true)
  69. .Where(i => string.Equals(Path.GetFileName(i), "device.json", StringComparison.OrdinalIgnoreCase))
  70. .ToList();
  71. }
  72. catch (IOException)
  73. {
  74. return;
  75. }
  76. foreach (var file in files)
  77. {
  78. try
  79. {
  80. var device = _json.DeserializeFromFile<DeviceInfo>(file);
  81. device.Name = string.IsNullOrWhiteSpace(device.CustomName) ? device.ReportedName : device.CustomName;
  82. SaveDevice(device);
  83. }
  84. catch (Exception ex)
  85. {
  86. Logger.ErrorException("Error reading {0}", ex, file);
  87. }
  88. finally
  89. {
  90. try
  91. {
  92. FileSystem.DeleteFile(file);
  93. }
  94. catch (IOException)
  95. {
  96. try
  97. {
  98. FileSystem.MoveFile(file, Path.ChangeExtension(file, ".old"));
  99. }
  100. catch (IOException)
  101. {
  102. }
  103. }
  104. }
  105. }
  106. }
  107. private const string BaseSelectText = "select Id, Name, ReportedName, CustomName, CameraUploadPath, LastUserName, AppName, AppVersion, LastUserId, DateLastModified, Capabilities from Devices";
  108. public void SaveCapabilities(string deviceId, ClientCapabilities capabilities)
  109. {
  110. using (WriteLock.Write())
  111. {
  112. using (var connection = CreateConnection())
  113. {
  114. connection.RunInTransaction(db =>
  115. {
  116. using (var statement = db.PrepareStatement("update devices set Capabilities=@Capabilities where Id=@Id"))
  117. {
  118. statement.TryBind("@Id", deviceId);
  119. if (capabilities == null)
  120. {
  121. statement.TryBindNull("@Capabilities");
  122. }
  123. else
  124. {
  125. statement.TryBind("@Capabilities", _json.SerializeToString(capabilities));
  126. }
  127. statement.MoveNext();
  128. }
  129. }, TransactionMode);
  130. }
  131. }
  132. }
  133. public void SaveDevice(DeviceInfo entry)
  134. {
  135. if (entry == null)
  136. {
  137. throw new ArgumentNullException("entry");
  138. }
  139. using (WriteLock.Write())
  140. {
  141. using (var connection = CreateConnection())
  142. {
  143. connection.RunInTransaction(db =>
  144. {
  145. using (var statement = db.PrepareStatement("replace into Devices (Id, Name, ReportedName, CustomName, CameraUploadPath, LastUserName, AppName, AppVersion, LastUserId, DateLastModified, Capabilities) values (@Id, @Name, @ReportedName, @CustomName, @CameraUploadPath, @LastUserName, @AppName, @AppVersion, @LastUserId, @DateLastModified, @Capabilities)"))
  146. {
  147. statement.TryBind("@Id", entry.Id);
  148. statement.TryBind("@Name", entry.Name);
  149. statement.TryBind("@ReportedName", entry.ReportedName);
  150. statement.TryBind("@CustomName", entry.CustomName);
  151. statement.TryBind("@CameraUploadPath", entry.CameraUploadPath);
  152. statement.TryBind("@LastUserName", entry.LastUserName);
  153. statement.TryBind("@AppName", entry.AppName);
  154. statement.TryBind("@AppVersion", entry.AppVersion);
  155. statement.TryBind("@DateLastModified", entry.DateLastModified);
  156. if (entry.Capabilities == null)
  157. {
  158. statement.TryBindNull("@Capabilities");
  159. }
  160. else
  161. {
  162. statement.TryBind("@Capabilities", _json.SerializeToString(entry.Capabilities));
  163. }
  164. statement.MoveNext();
  165. }
  166. }, TransactionMode);
  167. }
  168. }
  169. }
  170. public DeviceInfo GetDevice(string id)
  171. {
  172. using (WriteLock.Read())
  173. {
  174. using (var connection = CreateConnection(true))
  175. {
  176. var statementTexts = new List<string>();
  177. statementTexts.Add(BaseSelectText + " where Id=@Id");
  178. return connection.RunInTransaction(db =>
  179. {
  180. var statements = PrepareAllSafe(db, statementTexts).ToList();
  181. using (var statement = statements[0])
  182. {
  183. statement.TryBind("@Id", id);
  184. foreach (var row in statement.ExecuteQuery())
  185. {
  186. return GetEntry(row);
  187. }
  188. }
  189. return null;
  190. }, ReadTransactionMode);
  191. }
  192. }
  193. }
  194. public List<DeviceInfo> GetDevices()
  195. {
  196. using (WriteLock.Read())
  197. {
  198. using (var connection = CreateConnection(true))
  199. {
  200. var statementTexts = new List<string>();
  201. statementTexts.Add(BaseSelectText + " order by DateLastModified desc");
  202. return connection.RunInTransaction(db =>
  203. {
  204. var list = new List<DeviceInfo>();
  205. var statements = PrepareAllSafe(db, statementTexts).ToList();
  206. using (var statement = statements[0])
  207. {
  208. foreach (var row in statement.ExecuteQuery())
  209. {
  210. list.Add(GetEntry(row));
  211. }
  212. }
  213. return list;
  214. }, ReadTransactionMode);
  215. }
  216. }
  217. }
  218. public ClientCapabilities GetCapabilities(string id)
  219. {
  220. using (WriteLock.Read())
  221. {
  222. using (var connection = CreateConnection(true))
  223. {
  224. var statementTexts = new List<string>();
  225. statementTexts.Add("Select Capabilities from Devices where Id=@Id");
  226. return connection.RunInTransaction(db =>
  227. {
  228. var statements = PrepareAllSafe(db, statementTexts).ToList();
  229. using (var statement = statements[0])
  230. {
  231. statement.TryBind("@Id", id);
  232. foreach (var row in statement.ExecuteQuery())
  233. {
  234. if (row[0].SQLiteType != SQLiteType.Null)
  235. {
  236. return _json.DeserializeFromString<ClientCapabilities>(row.GetString(0));
  237. }
  238. }
  239. }
  240. return null;
  241. }, ReadTransactionMode);
  242. }
  243. }
  244. }
  245. private DeviceInfo GetEntry(IReadOnlyList<IResultSetValue> reader)
  246. {
  247. var index = 0;
  248. var info = new DeviceInfo
  249. {
  250. Id = reader.GetString(index)
  251. };
  252. index++;
  253. if (reader[index].SQLiteType != SQLiteType.Null)
  254. {
  255. info.Name = reader.GetString(index);
  256. }
  257. index++;
  258. if (reader[index].SQLiteType != SQLiteType.Null)
  259. {
  260. info.ReportedName = reader.GetString(index);
  261. }
  262. index++;
  263. if (reader[index].SQLiteType != SQLiteType.Null)
  264. {
  265. info.CustomName = reader.GetString(index);
  266. }
  267. index++;
  268. if (reader[index].SQLiteType != SQLiteType.Null)
  269. {
  270. info.CameraUploadPath = reader.GetString(index);
  271. }
  272. index++;
  273. if (reader[index].SQLiteType != SQLiteType.Null)
  274. {
  275. info.LastUserName = reader.GetString(index);
  276. }
  277. index++;
  278. if (reader[index].SQLiteType != SQLiteType.Null)
  279. {
  280. info.AppName = reader.GetString(index);
  281. }
  282. index++;
  283. if (reader[index].SQLiteType != SQLiteType.Null)
  284. {
  285. info.AppVersion = reader.GetString(index);
  286. }
  287. index++;
  288. if (reader[index].SQLiteType != SQLiteType.Null)
  289. {
  290. info.LastUserId = reader.GetString(index);
  291. }
  292. index++;
  293. if (reader[index].SQLiteType != SQLiteType.Null)
  294. {
  295. info.DateLastModified = reader[index].ReadDateTime();
  296. }
  297. index++;
  298. if (reader[index].SQLiteType != SQLiteType.Null)
  299. {
  300. info.Capabilities = _json.DeserializeFromString<ClientCapabilities>(reader.GetString(index));
  301. }
  302. return info;
  303. }
  304. private string GetDevicesPath()
  305. {
  306. return Path.Combine(_appPaths.DataPath, "devices");
  307. }
  308. private string GetDevicePath(string id)
  309. {
  310. return Path.Combine(GetDevicesPath(), id.GetMD5().ToString("N"));
  311. }
  312. public ContentUploadHistory GetCameraUploadHistory(string deviceId)
  313. {
  314. var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json");
  315. lock (_syncLock)
  316. {
  317. try
  318. {
  319. return _json.DeserializeFromFile<ContentUploadHistory>(path);
  320. }
  321. catch (IOException)
  322. {
  323. return new ContentUploadHistory
  324. {
  325. DeviceId = deviceId
  326. };
  327. }
  328. }
  329. }
  330. public void AddCameraUpload(string deviceId, LocalFileInfo file)
  331. {
  332. var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json");
  333. FileSystem.CreateDirectory(FileSystem.GetDirectoryName(path));
  334. lock (_syncLock)
  335. {
  336. ContentUploadHistory history;
  337. try
  338. {
  339. history = _json.DeserializeFromFile<ContentUploadHistory>(path);
  340. }
  341. catch (IOException)
  342. {
  343. history = new ContentUploadHistory
  344. {
  345. DeviceId = deviceId
  346. };
  347. }
  348. history.DeviceId = deviceId;
  349. var list = history.FilesUploaded.ToList();
  350. list.Add(file);
  351. history.FilesUploaded = list.ToArray(list.Count);
  352. _json.SerializeToFile(history, path);
  353. }
  354. }
  355. public void DeleteDevice(string id)
  356. {
  357. using (WriteLock.Write())
  358. {
  359. using (var connection = CreateConnection())
  360. {
  361. connection.RunInTransaction(db =>
  362. {
  363. using (var statement = db.PrepareStatement("delete from devices where Id=@Id"))
  364. {
  365. statement.TryBind("@Id", id);
  366. statement.MoveNext();
  367. }
  368. }, TransactionMode);
  369. }
  370. }
  371. var path = GetDevicePath(id);
  372. lock (_syncLock)
  373. {
  374. try
  375. {
  376. FileSystem.DeleteDirectory(path, true);
  377. }
  378. catch (IOException)
  379. {
  380. }
  381. }
  382. }
  383. }
  384. }