2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 using System.Collections.Generic;
21 using System.Threading;
22 using System.Threading.Tasks;
24 namespace Tizen.Content.MediaContent
27 /// Provides commands to manage the media information and query related items in the database.
29 public class MediaInfoCommand : MediaCommand
32 /// Initializes a new instance of the <see cref="FolderCommand"/> class with the specified <see cref="MediaDatabase"/>.
34 /// <param name="database">The <see cref="MediaDatabase"/> that the commands run on.</param>
35 /// <exception cref="ArgumentNullException"><paramref name="database"/> is null.</exception>
36 /// <exception cref="ObjectDisposedException"><paramref name="database"/> has already been disposed of.</exception>
37 public MediaInfoCommand(MediaDatabase database) : base(database)
42 /// Retrieves the number of the bookmarks added to the media.
44 /// <param name="mediaId">The media ID to count the bookmarks added to the media.</param>
45 /// <returns>The number of the bookmarks.</returns>
46 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
47 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
48 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
49 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
50 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
51 public int CountBookmark(string mediaId)
53 return CountBookmark(mediaId, null);
57 /// Retrieves the number of the bookmarks added to the media with the <see cref="CountArguments"/>.
59 /// <param name="mediaId">The media ID to count the bookmarks added to the media.</param>
60 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
61 /// <returns>The number of the bookmarks.</returns>
62 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
63 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
64 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
65 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
66 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
67 public int CountBookmark(string mediaId, CountArguments arguments)
71 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
73 return CommandHelper.Count(Interop.MediaInfo.GetBookmarkCount, mediaId, arguments);
77 /// Retrieves the bookmarks added to the media.
79 /// <param name="mediaId">The media ID to select the bookmarks added to the media.</param>
80 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
81 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
82 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
83 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
84 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
85 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
86 public MediaDataReader<Bookmark> SelectBookmark(string mediaId)
88 return SelectBookmark(mediaId, null);
92 /// Retrieves the bookmarks added to the media with the <see cref="SelectArguments"/>.
94 /// <param name="mediaId">The media ID to select the bookmarks added to the media.</param>
95 /// <param name="filter">The criteria to use to filter. This value can be null.</param>
96 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
97 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
98 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
99 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
100 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
101 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
102 public MediaDataReader<Bookmark> SelectBookmark(string mediaId, SelectArguments filter)
106 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
108 return CommandHelper.SelectMembers(mediaId, filter, Interop.MediaInfo.ForeachBookmarks,
109 Bookmark.FromHandle);
114 /// Retrieves the number of the face information added to or detected from the media.
116 /// <param name="mediaId">The media ID to count face information added to the media.</param>
117 /// <returns>The number of the face information.</returns>
118 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
119 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
120 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
121 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
122 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
123 public int CountFaceInfo(string mediaId)
125 return CountFaceInfo(mediaId, null);
129 /// Retrieves the number of the face information added to or detected from the media with filter.
131 /// <param name="mediaId">The media ID to count the face information added to the media.</param>
132 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
133 /// <returns>The number of the face information.</returns>
134 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
135 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
136 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
137 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
138 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
139 public int CountFaceInfo(string mediaId, CountArguments arguments)
143 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
145 return CommandHelper.Count(Interop.MediaInfo.GetFaceCount, mediaId, arguments);
149 /// Retrieves the face information added to or detected from the media.
151 /// <param name="mediaId">The media ID to select face information added to the media.</param>
152 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
153 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
154 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
155 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
156 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
157 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
158 public MediaDataReader<FaceInfo> SelectFaceInfo(string mediaId)
160 return SelectFaceInfo(mediaId, null);
164 /// Retrieves the face information added to or detected from the media with the <see cref="SelectArguments"/>.
166 /// <param name="mediaId">The media ID to select the face information added to the media.</param>
167 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
168 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
169 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
170 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
171 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
172 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
173 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
174 public MediaDataReader<FaceInfo> SelectFaceInfo(string mediaId, SelectArguments arguments)
178 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
180 return CommandHelper.SelectMembers(mediaId, arguments, Interop.MediaInfo.ForeachFaces,
181 FaceInfo.FromHandle);
185 /// Retrieves the number of tags that the media has.
187 /// <returns>The number of tags.</returns>
188 /// <param name="mediaId">The media ID to count tags added to the media.</param>
189 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
190 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
191 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
192 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
193 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
194 public int CountTag(string mediaId)
196 return CountTag(mediaId, null);
200 /// Retrieves the number of tags that the media has with the <see cref="CountArguments"/>.
202 /// <param name="mediaId">The media ID to count tags added to the media.</param>
203 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
204 /// <returns>The number of tags.</returns>
205 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
206 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
207 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
208 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
209 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
210 public int CountTag(string mediaId, CountArguments arguments)
214 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
216 return CommandHelper.Count(Interop.MediaInfo.GetTagCount, mediaId, arguments);
220 /// Retrieves the tags that the media has.
222 /// <param name="mediaId">The media ID to select tags added to the media.</param>
223 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
224 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
225 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
226 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
227 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
228 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
229 public MediaDataReader<Tag> SelectTag(string mediaId)
231 return SelectTag(mediaId, null);
235 /// Retrieves the tags that the media has with the <see cref="SelectArguments"/>.
237 /// <param name="mediaId">The media ID to select tags added to the media.</param>
238 /// <param name="filter">The criteria to use to filter. This value can be null.</param>
239 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
240 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
241 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
242 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
243 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
244 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
245 public MediaDataReader<Tag> SelectTag(string mediaId, SelectArguments filter)
249 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
251 return CommandHelper.SelectMembers(mediaId, filter, Interop.MediaInfo.ForeachTags,
257 /// Retrieves the number of the media information.
259 /// <returns>The number of the media information.</returns>
260 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
261 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
262 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
263 public int CountMedia()
265 return CountMedia(null);
269 /// Retrieves the number of the media information with the <see cref="SelectArguments"/>.
271 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
272 /// <returns>The number of media information.</returns>
273 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
274 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
275 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
276 public int CountMedia(CountArguments arguments)
280 return CommandHelper.Count(Interop.MediaInfo.GetMediaCount, arguments);
284 /// Retrieves the media.
286 /// <param name="mediaId">The media ID to retrieve.</param>
287 /// <returns>The <see cref="MediaInfo"/> instance if the matched record was found in the database, otherwise null.</returns>
288 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
289 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
290 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
291 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
292 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
293 public MediaInfo SelectMedia(string mediaId)
297 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
299 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).Ignore(MediaContentError.InvalidParameter).
300 ThrowIfError("Failed to query");
304 return MediaInfo.FromHandle(handle);
313 /// Retrieves the number of values grouped by the specified column with the <see cref="SelectArguments"/>.
315 /// <param name="columnKey">The column key.</param>
316 /// <returns>The number of groups.</returns>
317 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
318 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
319 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
320 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
321 public int CountGroupBy(MediaInfoColumnKey columnKey)
323 return CountGroupBy(columnKey, null);
327 /// Retrieves the number of values grouped by the specified column with the <see cref="SelectArguments"/>.
329 /// <param name="columnKey">The column key.</param>
330 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
331 /// <returns>The number of groups.</returns>
332 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
333 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
334 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
335 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
336 public int CountGroupBy(MediaInfoColumnKey columnKey, CountArguments arguments)
340 ValidationUtil.ValidateEnum(typeof(MediaInfoColumnKey), columnKey, nameof(columnKey));
342 using (var filter = QueryArguments.ToNativeHandle(arguments))
344 Interop.Group.GetGroupCount(filter, columnKey, out var count).ThrowIfError("Failed to query count");
350 /// Retrieves the group values of the specified column.
352 /// <param name="columnKey">The column key.</param>
353 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
354 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
355 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
356 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
357 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
358 public MediaDataReader<string> SelectGroupBy(MediaInfoColumnKey columnKey)
360 return SelectGroupBy(columnKey, null);
364 /// Retrieves the group values of the specified column with the <see cref="SelectArguments"/>.
366 /// <param name="columnKey">The column key.</param>
367 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
368 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
369 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
370 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
371 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
372 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
373 public MediaDataReader<string> SelectGroupBy(MediaInfoColumnKey columnKey, SelectArguments arguments)
377 ValidationUtil.ValidateEnum(typeof(MediaInfoColumnKey), columnKey, nameof(columnKey));
379 List<string> list = new List<string>();
381 using (var filter = QueryArguments.ToNativeHandle(arguments))
383 Interop.Group.ForeachGroup(filter, columnKey, (name, _) =>
388 }).ThrowIfError("Failed to query");
390 return new MediaDataReader<string>(list);
395 /// Retrieves all the media.
397 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
398 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
399 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
400 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
401 public MediaDataReader<MediaInfo> SelectMedia()
403 return SelectMedia(arguments: null);
407 /// Retrieves the media with the <see cref="SelectArguments"/>.
409 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
410 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
411 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
412 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
413 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
414 public MediaDataReader<MediaInfo> SelectMedia(SelectArguments arguments)
418 return new MediaDataReader<MediaInfo>(QueryMedia(arguments));
421 private static List<MediaInfo> QueryMedia(SelectArguments arguments)
423 using (var filter = QueryArguments.ToNativeHandle(arguments))
425 List<MediaInfo> list = new List<MediaInfo>();
427 Exception caught = null;
429 Interop.MediaInfo.ForeachMedia(filter, (handle, _) =>
433 list.Add(MediaInfo.FromHandle(handle));
453 /// Deletes the media from the database.
455 /// <privilege>http://tizen.org/privilege/content.write</privilege>
456 /// <param name="mediaId">The media ID to delete.</param>
457 /// <returns>true if the matched record was found and deleted, otherwise false.</returns>
458 /// <remarks>The <see cref="MediaDatabase.ScanFile(string)"/> or the <see cref="MediaDatabase.ScanFolderAsync(string)"/> can be used instead.</remarks>
459 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
460 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
461 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
462 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
463 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
464 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
465 public bool Delete(string mediaId)
469 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
471 if (CommandHelper.Count(
472 Interop.MediaInfo.GetMediaCount, $"{MediaInfoColumns.Id}='{mediaId}'") == 0)
477 CommandHelper.Delete(Interop.MediaInfo.Delete, mediaId);
482 /// Adds the media to the database.
484 /// <param name="path">The file path to add.</param>
485 /// <returns>The <see cref="MediaInfo"/> instance that contains the record information in the database.</returns>
487 /// If the media already exists in the database, it returns the existing information.\n
489 /// The <see cref="MediaDatabase.ScanFile(string)"/> or the <see cref="MediaDatabase.ScanFolderAsync(string)"/> can be used instead.\n
491 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.\n
492 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
494 /// <privilege>http://tizen.org/privilege/content.write</privilege>
495 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
496 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
497 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
498 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
499 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
500 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
501 /// <exception cref="ArgumentException">
502 /// <paramref name="path"/> is a zero-length string, contains only white space.\n
504 /// <paramref name="path"/> contains a hidden path that starts with '.'.\n
506 /// <paramref name="path"/> contains a directory containing the ".scan_ignore" file.
508 /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exists.</exception>
509 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
510 public MediaInfo Add(string path)
514 ValidationUtil.ValidateNotNullOrEmpty(path, nameof(path));
516 if (File.Exists(path) == false)
518 throw new FileNotFoundException("destination is not valid path.", path);
521 if (File.GetAttributes(path).HasFlag(FileAttributes.Hidden))
523 throw new ArgumentException($"{nameof(path)} contains a hidden path.", nameof(path));
526 Interop.MediaInfoHandle handle = null;
530 Interop.MediaInfo.Insert(path, out handle).ThrowIfError("Failed to insert");
532 return MediaInfo.FromHandle(handle);
543 private static void ValidatePaths(IEnumerable<string> paths)
547 throw new ArgumentNullException(nameof(paths));
550 if (paths.Count() > 300)
552 throw new ArgumentException("Too many paths to add.");
555 foreach (var path in paths)
559 throw new ArgumentException($"{nameof(paths)} contains null.", nameof(paths));
562 if (File.Exists(path) == false)
564 throw new FileNotFoundException($"{nameof(paths)} contains a path that does not exist. Path={path}.", path);
571 /// Adds media files into the media database.
574 /// The paths that already exist in the database will be ignored.\n
575 /// At most 300 items can be added at once.\n
577 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.\n
578 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
580 /// <privilege>http://tizen.org/privilege/content.write</privilege>
581 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
582 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
583 /// <param name="paths">The paths of the media files to add.</param>
584 /// <returns>A task that represents the asynchronous add operation.</returns>
585 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
586 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
587 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
588 /// <exception cref="ArgumentNullException"><paramref name="paths"/> is null.</exception>
589 /// <exception cref="ArgumentException">
590 /// <paramref name="paths"/> contains null.\n
592 /// <paramref name="paths"/> contains the invalid path.\n
594 /// The number of <paramref name="paths"/> is 300 or more items.
596 /// <exception cref="FileNotFoundException"><paramref name="paths"/> contains a path that does not exist.</exception>
597 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
598 public async Task AddAsync(IEnumerable<string> paths)
602 ValidatePaths(paths);
604 var pathArray = paths.ToArray();
605 var tcs = new TaskCompletionSource<bool>(TaskContinuationOptions.RunContinuationsAsynchronously);
607 Interop.MediaInfo.InsertCompletedCallback callback = (error, _) =>
609 if (error == MediaContentError.None)
611 tcs.TrySetResult(true);
615 tcs.TrySetException(error.AsException("Failed to add"));
619 using (ObjectKeeper.Get(callback))
621 Interop.MediaInfo.BatchInsert(pathArray, pathArray.Length, callback).ThrowIfError("Failed to add");
628 /// Adds burst shot images into the media database.
630 /// <param name="paths">The paths of the burst shot images to add.</param>
631 /// <returns>A task that represents the asynchronous add operation.</returns>
633 /// The paths that already exist in the database.\n
634 /// At most 300 items can be added at once.
636 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.\n
637 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
639 /// <privilege>http://tizen.org/privilege/content.write</privilege>
640 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
641 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
642 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
643 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
644 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
645 /// <exception cref="ArgumentNullException"><paramref name="paths"/> is null.</exception>
646 /// <exception cref="ArgumentException">
647 /// <paramref name="paths"/> contains null.\n
649 /// <paramref name="paths"/> contains the invalid path.\n
651 /// The number of <paramref name="paths"/> is 300 or more items.
653 /// <exception cref="FileNotFoundException"><paramref name="paths"/> contains a path that does not exist.</exception>
654 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
655 public async Task AddBurstShotImagesAsync(IEnumerable<string> paths)
659 ValidatePaths(paths);
661 var tcs = new TaskCompletionSource<bool>(TaskContinuationOptions.RunContinuationsAsynchronously);
662 string[] pathArray = paths.ToArray();
664 Interop.MediaInfo.InsertBurstShotCompletedCallback callback = (error, _) =>
666 if (error == MediaContentError.None)
668 tcs.TrySetResult(true);
672 tcs.TrySetException(error.AsException("Failed to add burst shot images"));
676 using (ObjectKeeper.Get(callback))
678 Interop.MediaInfo.BurstShotInsert(pathArray, pathArray.Length, callback).
679 ThrowIfError("Failed to add burst shots to db");
685 private static void SetUpdateValue<T>(Interop.MediaInfoHandle handle, T value,
686 Func<Interop.MediaInfoHandle, T, MediaContentError> func)
690 func(handle, value).ThrowIfError("Failed to update");
695 private static void SetUpdateValue<T>(Interop.MediaInfoHandle handle, Nullable<T> value,
696 Func<Interop.MediaInfoHandle, T, MediaContentError> func) where T : struct
700 func(handle, value.Value).ThrowIfError("Failed to update");
705 /// Updates the media with the specified values.
707 /// <privilege>http://tizen.org/privilege/content.write</privilege>
708 /// <param name="mediaId">The media ID to update.</param>
709 /// <param name="values">The values for update.</param>
710 /// <returns>true if the matched record was found and updated, otherwise false.</returns>
711 /// <remarks>Only values set in the <see cref="MediaInfoUpdateValues"/> are updated.</remarks>
712 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
713 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
714 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
715 /// <exception cref="ArgumentNullException">
716 /// <paramref name="mediaId"/> is null.\n
718 /// <paramref name="values"/> is null.
720 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
721 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
722 public bool Update(string mediaId, MediaInfoUpdateValues values)
726 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
730 throw new ArgumentNullException(nameof(values));
733 if (CommandHelper.Count(
734 Interop.MediaInfo.GetMediaCount, $"{MediaInfoColumns.Id}='{mediaId}'") == 0)
739 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to update");
741 if (handle.IsInvalid)
748 SetUpdateValue(handle, values.Weather, Interop.MediaInfo.SetWeather);
749 SetUpdateValue(handle, values.IsFavorite, Interop.MediaInfo.SetFavorite);
750 SetUpdateValue(handle, values.Provider, Interop.MediaInfo.SetProvider);
751 SetUpdateValue(handle, values.Category, Interop.MediaInfo.SetCategory);
752 SetUpdateValue(handle, values.LocationTag, Interop.MediaInfo.SetLocationTag);
753 SetUpdateValue(handle, values.AgeRating, Interop.MediaInfo.SetAgeRating);
755 Interop.MediaInfo.UpdateToDB(handle).ThrowIfError("Failed to update");
765 /// Updates the path of the media to the specified destination path in the database.
767 /// <privilege>http://tizen.org/privilege/content.write</privilege>
768 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
769 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
770 /// <param name="mediaId">The media ID to move.</param>
771 /// <param name="newPath">The path that the media has been moved to.</param>
772 /// <returns>true if the matched record was found and updated, otherwise false.</returns>
774 /// Usually, it is used after the media file is moved to the another path.\n
776 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.\n
777 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
779 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
780 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
781 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
782 /// <exception cref="ArgumentNullException">
783 /// <paramref name="mediaId"/> is null.\n
785 /// <paramref name="newPath"/> is null.
787 /// <exception cref="ArgumentException">
788 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.\n
790 /// <paramref name="newPath"/> is a zero-length string, contains only white space.\n
792 /// <paramref name="newPath"/> contains a hidden directory that starts with '.'.\n
794 /// <paramref name="newPath"/> contains a directory containing the ".scan_ignore" file.
796 /// <exception cref="FileNotFoundException"><paramref name="newPath"/> does not exists.</exception>
797 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
798 public bool Move(string mediaId, string newPath)
802 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
804 ValidationUtil.ValidateNotNullOrEmpty(newPath, nameof(newPath));
806 if (File.Exists(newPath) == false)
808 throw new FileNotFoundException("destination is not valid path.", newPath);
811 if (File.GetAttributes(newPath).HasFlag(FileAttributes.Hidden))
813 throw new ArgumentException($"{nameof(newPath)} contains a hidden path.", nameof(newPath));
816 //TODO can be improved if MoveToDB supports result value.
817 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).
818 Ignore(MediaContentError.InvalidParameter).ThrowIfError("Failed to move");
820 if (handle.IsInvalid)
827 Interop.MediaInfo.MoveToDB(handle, newPath).ThrowIfError("Failed to move");
837 #region CreateThumbnailAsync
839 /// Creates the thumbnail image for the given media.
840 /// If the thumbnail already exists for the given media, the existing path will be returned.
842 /// <privilege>http://tizen.org/privilege/content.write</privilege>
843 /// <param name="mediaId">The media ID to create the thumbnail.</param>
844 /// <returns>A task that represents the asynchronous operation. The task result contains the thumbnail path.</returns>
845 /// <exception cref="InvalidOperationException">
846 /// The <see cref="MediaDatabase"/> is disconnected.\n
848 /// An internal error occurred while executing.
850 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
851 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
852 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
853 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
854 /// <exception cref="ArgumentException">
855 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
857 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
858 /// <exception cref="UnsupportedContentException">
859 /// The thumbnail is not available for the given media.\n
861 /// The media is in the external USB storage (<see cref="MediaInfo.StorageType"/> is <see cref="StorageType.ExternalUsb"/>).
863 public Task<string> CreateThumbnailAsync(string mediaId)
865 return CreateThumbnailAsync(mediaId, CancellationToken.None);
869 /// Creates the thumbnail image for the given media.
870 /// If the thumbnail already exists for the given media, the existing path will be returned.
872 /// <privilege>http://tizen.org/privilege/content.write</privilege>
873 /// <param name="mediaId">The media ID to create the thumbnail.</param>
874 /// <param name="cancellationToken">The token to cancel the operation.</param>
875 /// <returns>A task that represents the asynchronous operation. The task result contains the thumbnail path.</returns>
876 /// <exception cref="InvalidOperationException">
877 /// The <see cref="MediaDatabase"/> is disconnected.\n
879 /// An internal error occurred while executing.
881 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
882 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
883 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
884 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
885 /// <exception cref="ArgumentException">
886 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
888 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
889 /// <exception cref="UnsupportedContentException">
890 /// The thumbnail is not available for the given media.\n
892 /// The media is in the external USB storage (<see cref="MediaInfo.StorageType"/> is <see cref="StorageType.ExternalUsb"/>).
894 public Task<string> CreateThumbnailAsync(string mediaId, CancellationToken cancellationToken)
898 return cancellationToken.IsCancellationRequested ? Task.FromCanceled<string>(cancellationToken) :
899 CreateThumbnailAsyncCore(mediaId, cancellationToken);
902 private async Task<string> CreateThumbnailAsyncCore(string mediaId, CancellationToken cancellationToken)
904 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
906 var tcs = new TaskCompletionSource<string>();
908 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to create thumbnail");
910 if (handle.IsInvalid)
912 throw new RecordNotFoundException("Media does not exist.");
917 if (InteropHelper.GetValue<StorageType>(handle, Interop.MediaInfo.GetStorageType) == StorageType.ExternalUsb)
919 throw new UnsupportedContentException("The media is in external usb storage.");
922 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
924 if (File.Exists(path) == false)
926 throw new FileNotFoundException($"The media file does not exist. Path={path}.", path);
929 using (RegisterCancelThumbnail(cancellationToken, tcs, handle))
930 using (var cbKeeper = ObjectKeeper.Get(GetCreateThumbnailCallback(tcs)))
932 Interop.MediaInfo.CreateThumbnail(handle, cbKeeper.Target).ThrowIfError("Failed to create thumbnail");
934 return await tcs.Task;
939 private static Interop.MediaInfo.ThumbnailCompletedCallback GetCreateThumbnailCallback(
940 TaskCompletionSource<string> tcs)
942 return (error, path, _) =>
944 if (error != MediaContentError.None)
946 tcs.TrySetException(error.AsException("Failed to create thumbnail"));
950 tcs.TrySetResult(path);
955 private static IDisposable RegisterCancelThumbnail(CancellationToken cancellationToken,
956 TaskCompletionSource<string> tcs, Interop.MediaInfoHandle handle)
958 if (cancellationToken.CanBeCanceled == false)
963 return cancellationToken.Register(() =>
965 if (tcs.Task.IsCompleted)
970 Interop.MediaInfo.CancelThumbnail(handle).ThrowIfError("Failed to cancel");
971 tcs.TrySetCanceled();
976 #region DetectFaceAsync
978 /// Detects faces from the given media.
979 /// If the thumbnail already exists for the given media, the existing path will be returned.
981 /// <privilege>http://tizen.org/privilege/content.write</privilege>
982 /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
983 /// <param name="mediaId">The media ID to create the thumbnail.</param>
984 /// <returns>A task that represents the asynchronous add operation. The task result contains the number of faces detected.</returns>
985 /// <exception cref="InvalidOperationException">
986 /// The <see cref="MediaDatabase"/> is disconnected.\n
988 /// An internal error occurred while executing.
990 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
991 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
992 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
993 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
994 /// <exception cref="ArgumentException">
995 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
997 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
998 /// <exception cref="UnsupportedContentException">Face detection is not available for the given media.</exception>
999 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
1000 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
1001 public Task<int> DetectFaceAsync(string mediaId)
1003 return DetectFaceAsync(mediaId, CancellationToken.None);
1007 /// Creates the thumbnail image for the given media.
1008 /// If the thumbnail already exists for the given media, the existing path will be returned.
1011 /// Media in the external storage is not supported, with the exception of MMC.
1013 /// <privilege>http://tizen.org/privilege/content.write</privilege>
1014 /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
1015 /// <param name="mediaId">The media ID to create the thumbnail.</param>
1016 /// <param name="cancellationToken">The token to cancel the operation.</param>
1017 /// <returns>A task that represents the asynchronous operation. The task result contains the number of faces detected.</returns>
1018 /// <exception cref="InvalidOperationException">
1019 /// The <see cref="MediaDatabase"/> is disconnected.\n
1021 /// An internal error occurred while executing.
1023 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
1024 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
1025 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
1026 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
1027 /// <exception cref="ArgumentException">
1028 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
1030 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
1031 /// <exception cref="UnsupportedContentException">
1032 /// Face detection is not available for the given media.\n
1034 /// The media is in the external USB storage (<see cref="MediaInfo.StorageType"/> is <see cref="StorageType.ExternalUsb"/>).
1036 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
1037 public Task<int> DetectFaceAsync(string mediaId, CancellationToken cancellationToken)
1039 if (Features.IsSupported(Features.FaceRecognition) == false)
1041 throw new NotSupportedException($"The feature({Features.FaceRecognition}) is not supported.");
1046 return cancellationToken.IsCancellationRequested ? Task.FromCanceled<int>(cancellationToken) :
1047 DetectFaceAsyncCore(mediaId, cancellationToken);
1050 private static async Task<int> DetectFaceAsyncCore(string mediaId, CancellationToken cancellationToken)
1052 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
1054 var tcs = new TaskCompletionSource<int>();
1056 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to detect faces");
1058 if (handle.IsInvalid)
1060 throw new RecordNotFoundException("Media does not exist.");
1065 if (InteropHelper.GetValue<StorageType>(handle, Interop.MediaInfo.GetStorageType) == StorageType.ExternalUsb)
1067 throw new UnsupportedContentException("The media is in external usb storage.");
1070 if (InteropHelper.GetValue<MediaType>(handle, Interop.MediaInfo.GetMediaType) != MediaType.Image)
1072 throw new UnsupportedContentException("Only image is supported.");
1075 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
1077 if (File.Exists(path) == false)
1079 throw new FileNotFoundException($"The media file does not exist. Path={path}.", path);
1082 using (RegisterCancelFaceDetection(cancellationToken, tcs, handle))
1083 using (var cbKeeper = ObjectKeeper.Get(GetFaceDetectionCallback(tcs)))
1085 Interop.MediaInfo.StartFaceDetection(handle, cbKeeper.Target).ThrowIfError("Failed to detect faces");
1087 return await tcs.Task;
1092 private static Interop.MediaInfo.FaceDetectionCompletedCallback GetFaceDetectionCallback(
1093 TaskCompletionSource<int> tcs)
1095 return (error, count, _) =>
1097 if (error != MediaContentError.None)
1099 tcs.TrySetException(error.AsException("Failed to detect faces"));
1103 tcs.TrySetResult(count);
1108 private static IDisposable RegisterCancelFaceDetection(CancellationToken cancellationToken,
1109 TaskCompletionSource<int> tcs, Interop.MediaInfoHandle handle)
1111 if (cancellationToken.CanBeCanceled == false)
1116 return cancellationToken.Register(() =>
1118 if (tcs.Task.IsCompleted)
1123 Interop.MediaInfo.CancelFaceDetection(handle).ThrowIfError("Failed to cancel");
1124 tcs.TrySetCanceled();