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);
113 /// Retrieves the number of the face information added to or detected from the media.
115 /// <param name="mediaId">The media ID to count face information added to the media.</param>
116 /// <returns>The number of the face information.</returns>
117 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
118 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
119 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
120 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
121 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
122 public int CountFaceInfo(string mediaId)
124 return CountFaceInfo(mediaId, null);
128 /// Retrieves the number of the face information added to or detected from the media with filter.
130 /// <param name="mediaId">The media ID to count the face information added to the media.</param>
131 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
132 /// <returns>The number of the face information.</returns>
133 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
134 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
135 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
136 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
137 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
138 public int CountFaceInfo(string mediaId, CountArguments arguments)
142 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
144 return CommandHelper.Count(Interop.MediaInfo.GetFaceCount, mediaId, arguments);
148 /// Retrieves the face information added to or detected from the media.
150 /// <param name="mediaId">The media ID to select face information added to the media.</param>
151 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
152 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
153 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
154 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
155 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
156 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
157 public MediaDataReader<FaceInfo> SelectFaceInfo(string mediaId)
159 return SelectFaceInfo(mediaId, null);
163 /// Retrieves the face information added to or detected from the media with the <see cref="SelectArguments"/>.
165 /// <param name="mediaId">The media ID to select the face information added to the media.</param>
166 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
167 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
168 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
169 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
170 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
171 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
172 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
173 public MediaDataReader<FaceInfo> SelectFaceInfo(string mediaId, SelectArguments arguments)
177 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
179 return CommandHelper.SelectMembers(mediaId, arguments, Interop.MediaInfo.ForeachFaces,
180 FaceInfo.FromHandle);
184 /// Retrieves the number of tags that the media has.
186 /// <returns>The number of tags.</returns>
187 /// <param name="mediaId">The media ID to count tags added to the media.</param>
188 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
189 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
190 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
191 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
192 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
193 public int CountTag(string mediaId)
195 return CountTag(mediaId, null);
199 /// Retrieves the number of tags that the media has with the <see cref="CountArguments"/>.
201 /// <param name="mediaId">The media ID to count tags added to the media.</param>
202 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
203 /// <returns>The number of tags.</returns>
204 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
205 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
206 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
207 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
208 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
209 public int CountTag(string mediaId, CountArguments arguments)
213 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
215 return CommandHelper.Count(Interop.MediaInfo.GetTagCount, mediaId, arguments);
219 /// Retrieves the tags that the media has.
221 /// <param name="mediaId">The media ID to select tags added to the media.</param>
222 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
223 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
224 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
225 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
226 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
227 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
228 public MediaDataReader<Tag> SelectTag(string mediaId)
230 return SelectTag(mediaId, null);
234 /// Retrieves the tags that the media has with the <see cref="SelectArguments"/>.
236 /// <param name="mediaId">The media ID to select tags added to the media.</param>
237 /// <param name="filter">The criteria to use to filter. This value can be null.</param>
238 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
239 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
240 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
241 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
242 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
243 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
244 public MediaDataReader<Tag> SelectTag(string mediaId, SelectArguments filter)
248 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
250 return CommandHelper.SelectMembers(mediaId, filter, Interop.MediaInfo.ForeachTags,
255 /// Retrieves the number of the media information.
257 /// <returns>The number of the media information.</returns>
258 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
259 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
260 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
261 public int CountMedia()
263 return CountMedia(null);
267 /// Retrieves the number of the media information with the <see cref="SelectArguments"/>.
269 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
270 /// <returns>The number of media information.</returns>
271 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
272 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
273 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
274 public int CountMedia(CountArguments arguments)
278 return CommandHelper.Count(Interop.MediaInfo.GetMediaCount, arguments);
282 /// Retrieves the media.
284 /// <param name="mediaId">The media ID to retrieve.</param>
285 /// <returns>The <see cref="MediaInfo"/> instance if the matched record was found in the database, otherwise null.</returns>
286 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
287 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
288 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
289 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
290 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
291 public MediaInfo SelectMedia(string mediaId)
295 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
297 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).Ignore(MediaContentError.InvalidParameter).
298 ThrowIfError("Failed to query");
302 return MediaInfo.FromHandle(handle);
311 /// Retrieves the number of values grouped by the specified column with the <see cref="SelectArguments"/>.
313 /// <param name="columnKey">The column key.</param>
314 /// <returns>The number of groups.</returns>
315 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
316 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
317 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
318 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
319 public int CountGroupBy(MediaInfoColumnKey columnKey)
321 return CountGroupBy(columnKey, null);
325 /// Retrieves the number of values grouped by the specified column with the <see cref="SelectArguments"/>.
327 /// <param name="columnKey">The column key.</param>
328 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
329 /// <returns>The number of groups.</returns>
330 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
331 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
332 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
333 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
334 public int CountGroupBy(MediaInfoColumnKey columnKey, CountArguments arguments)
338 ValidationUtil.ValidateEnum(typeof(MediaInfoColumnKey), columnKey, nameof(columnKey));
340 using (var filter = QueryArguments.ToNativeHandle(arguments))
342 Interop.Group.GetGroupCount(filter, columnKey, out var count).ThrowIfError("Failed to query count");
348 /// Retrieves the group values of the specified column.
350 /// <param name="columnKey">The column key.</param>
351 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
352 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
353 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
354 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
355 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
356 public MediaDataReader<string> SelectGroupBy(MediaInfoColumnKey columnKey)
358 return SelectGroupBy(columnKey, null);
362 /// Retrieves the group values of the specified column with the <see cref="SelectArguments"/>.
364 /// <param name="columnKey">The column key.</param>
365 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
366 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
367 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
368 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
369 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
370 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
371 public MediaDataReader<string> SelectGroupBy(MediaInfoColumnKey columnKey, SelectArguments arguments)
375 ValidationUtil.ValidateEnum(typeof(MediaInfoColumnKey), columnKey, nameof(columnKey));
377 List<string> list = new List<string>();
379 using (var filter = QueryArguments.ToNativeHandle(arguments))
381 Interop.Group.ForeachGroup(filter, columnKey, (name, _) =>
386 }).ThrowIfError("Failed to query");
388 return new MediaDataReader<string>(list);
393 /// Retrieves all the media.
395 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
396 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
397 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
398 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
399 public MediaDataReader<MediaInfo> SelectMedia()
401 return SelectMedia(arguments: null);
405 /// Retrieves the media with the <see cref="SelectArguments"/>.
407 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
408 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
409 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
410 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
411 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
412 public MediaDataReader<MediaInfo> SelectMedia(SelectArguments arguments)
416 return new MediaDataReader<MediaInfo>(QueryMedia(arguments));
419 private static List<MediaInfo> QueryMedia(SelectArguments arguments)
421 using (var filter = QueryArguments.ToNativeHandle(arguments))
423 List<MediaInfo> list = new List<MediaInfo>();
425 Exception caught = null;
427 Interop.MediaInfo.ForeachMedia(filter, (handle, _) =>
431 list.Add(MediaInfo.FromHandle(handle));
451 /// Deletes the media from the database.
453 /// <privilege>http://tizen.org/privilege/content.write</privilege>
454 /// <param name="mediaId">The media ID to delete.</param>
455 /// <returns>true if the matched record was found and deleted, otherwise false.</returns>
456 /// <remarks>The <see cref="MediaDatabase.ScanFile(string)"/> or the <see cref="MediaDatabase.ScanFolderAsync(string)"/> can be used instead.</remarks>
457 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
458 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
459 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
460 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
461 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
462 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
463 public bool Delete(string mediaId)
467 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
469 if (CommandHelper.Count(
470 Interop.MediaInfo.GetMediaCount, $"{MediaInfoColumns.Id}='{mediaId}'") == 0)
475 CommandHelper.Delete(Interop.MediaInfo.Delete, mediaId);
480 /// Adds the media to the database.
482 /// <param name="path">The file path to add.</param>
483 /// <returns>The <see cref="MediaInfo"/> instance that contains the record information in the database.</returns>
485 /// If the media already exists in the database, it returns the existing information.\n
487 /// The <see cref="MediaDatabase.ScanFile(string)"/> or the <see cref="MediaDatabase.ScanFolderAsync(string)"/> can be used instead.\n
489 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.\n
490 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
492 /// <privilege>http://tizen.org/privilege/content.write</privilege>
493 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
494 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
495 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
496 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
497 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
498 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
499 /// <exception cref="ArgumentException">
500 /// <paramref name="path"/> is a zero-length string, contains only white space.\n
502 /// <paramref name="path"/> contains a hidden path that starts with '.'.\n
504 /// <paramref name="path"/> contains a directory containing the ".scan_ignore" file.
506 /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exists.</exception>
507 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
508 public MediaInfo Add(string path)
512 ValidationUtil.ValidateNotNullOrEmpty(path, nameof(path));
514 if (File.Exists(path) == false)
516 throw new FileNotFoundException("destination is not valid path.", path);
519 if (File.GetAttributes(path).HasFlag(FileAttributes.Hidden))
521 throw new ArgumentException($"{nameof(path)} contains a hidden path.", nameof(path));
524 Interop.MediaInfoHandle handle = null;
528 Interop.MediaInfo.Insert(path, out handle).ThrowIfError("Failed to insert");
530 return MediaInfo.FromHandle(handle);
541 private static void ValidatePaths(IEnumerable<string> paths)
545 throw new ArgumentNullException(nameof(paths));
548 if (paths.Count() > 300)
550 throw new ArgumentException("Too many paths to add.");
553 foreach (var path in paths)
557 throw new ArgumentException($"{nameof(paths)} contains null.", nameof(paths));
560 if (File.Exists(path) == false)
562 throw new FileNotFoundException($"{nameof(paths)} contains a path that does not exist. Path={path}.", path);
569 /// Adds media files into the media database.
572 /// The paths that already exist in the database will be ignored.\n
573 /// At most 300 items can be added at once.\n
575 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.\n
576 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
578 /// <privilege>http://tizen.org/privilege/content.write</privilege>
579 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
580 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
581 /// <param name="paths">The paths of the media files to add.</param>
582 /// <returns>A task that represents the asynchronous add operation.</returns>
583 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
584 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
585 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
586 /// <exception cref="ArgumentNullException"><paramref name="paths"/> is null.</exception>
587 /// <exception cref="ArgumentException">
588 /// <paramref name="paths"/> contains null.\n
590 /// <paramref name="paths"/> contains the invalid path.\n
592 /// The number of <paramref name="paths"/> is 300 or more items.
594 /// <exception cref="FileNotFoundException"><paramref name="paths"/> contains a path that does not exist.</exception>
595 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
596 public async Task AddAsync(IEnumerable<string> paths)
600 ValidatePaths(paths);
602 var pathArray = paths.ToArray();
603 var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
605 Interop.MediaInfo.InsertCompletedCallback callback = (error, _) =>
607 if (error == MediaContentError.None)
609 tcs.TrySetResult(true);
613 tcs.TrySetException(error.AsException("Failed to add"));
617 using (ObjectKeeper.Get(callback))
619 Interop.MediaInfo.BatchInsert(pathArray, pathArray.Length, callback).ThrowIfError("Failed to add");
626 /// Updates the media with the favorite value.
628 /// <privilege>http://tizen.org/privilege/content.write</privilege>
629 /// <param name="mediaId">The media ID to update.</param>
630 /// <param name="value">The value indicating whether the media is favorite.</param>
631 /// <returns>true if the matched record was found and updated, otherwise false.</returns>
632 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
633 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
634 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
635 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
636 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
637 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
638 public bool UpdateFavorite(string mediaId, bool value)
642 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
644 if (CommandHelper.Count(
645 Interop.MediaInfo.GetMediaCount, $"{MediaInfoColumns.Id}='{mediaId}'") == 0)
650 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to update");
652 if (handle.IsInvalid)
659 Interop.MediaInfo.SetFavorite(handle, value).ThrowIfError("Failed to update");
661 Interop.MediaInfo.UpdateToDB(handle).ThrowIfError("Failed to update");
671 /// Updates the path of the media to the specified destination path in the database.
673 /// <privilege>http://tizen.org/privilege/content.write</privilege>
674 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
675 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
676 /// <param name="mediaId">The media ID to move.</param>
677 /// <param name="newPath">The path that the media has been moved to.</param>
678 /// <returns>true if the matched record was found and updated, otherwise false.</returns>
680 /// Usually, it is used after the media file is moved to the another path.\n
682 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.\n
683 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
685 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
686 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
687 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
688 /// <exception cref="ArgumentNullException">
689 /// <paramref name="mediaId"/> is null.\n
691 /// <paramref name="newPath"/> is null.
693 /// <exception cref="ArgumentException">
694 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.\n
696 /// <paramref name="newPath"/> is a zero-length string, contains only white space.\n
698 /// <paramref name="newPath"/> contains a hidden directory that starts with '.'.\n
700 /// <paramref name="newPath"/> contains a directory containing the ".scan_ignore" file.
702 /// <exception cref="FileNotFoundException"><paramref name="newPath"/> does not exists.</exception>
703 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
704 public bool Move(string mediaId, string newPath)
708 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
710 ValidationUtil.ValidateNotNullOrEmpty(newPath, nameof(newPath));
712 if (File.Exists(newPath) == false)
714 throw new FileNotFoundException("destination is not valid path.", newPath);
717 if (File.GetAttributes(newPath).HasFlag(FileAttributes.Hidden))
719 throw new ArgumentException($"{nameof(newPath)} contains a hidden path.", nameof(newPath));
722 //TODO can be improved if MoveToDB supports result value.
723 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).
724 Ignore(MediaContentError.InvalidParameter).ThrowIfError("Failed to move");
726 if (handle.IsInvalid)
733 Interop.MediaInfo.MoveToDB(handle, newPath).ThrowIfError("Failed to move");
743 #region CreateThumbnailAsync
745 /// Creates the thumbnail image for the given media.
746 /// If the thumbnail already exists for the given media, the existing path will be returned.
748 /// <privilege>http://tizen.org/privilege/content.write</privilege>
749 /// <param name="mediaId">The media ID to create the thumbnail.</param>
750 /// <returns>A task that represents the asynchronous operation. The task result contains the thumbnail path.</returns>
751 /// <exception cref="InvalidOperationException">
752 /// The <see cref="MediaDatabase"/> is disconnected.\n
754 /// An internal error occurred while executing.
756 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
757 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
758 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
759 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
760 /// <exception cref="ArgumentException">
761 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
763 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
764 /// <exception cref="UnsupportedContentException">
765 /// The thumbnail is not available for the given media.\n
767 /// The media is in the external USB storage (<see cref="MediaInfo.StorageType"/> is <see cref="StorageType.ExternalUsb"/>).
769 public Task<string> CreateThumbnailAsync(string mediaId)
771 return CreateThumbnailAsync(mediaId, CancellationToken.None);
775 /// Creates the thumbnail image for the given media.
776 /// If the thumbnail already exists for the given media, the existing path will be returned.
778 /// <privilege>http://tizen.org/privilege/content.write</privilege>
779 /// <param name="mediaId">The media ID to create the thumbnail.</param>
780 /// <param name="cancellationToken">The token to cancel the operation.</param>
781 /// <returns>A task that represents the asynchronous operation. The task result contains the thumbnail path.</returns>
782 /// <exception cref="InvalidOperationException">
783 /// The <see cref="MediaDatabase"/> is disconnected.\n
785 /// An internal error occurred while executing.
787 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
788 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
789 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
790 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
791 /// <exception cref="ArgumentException">
792 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
794 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
795 /// <exception cref="UnsupportedContentException">
796 /// The thumbnail is not available for the given media.\n
798 /// The media is in the external USB storage (<see cref="MediaInfo.StorageType"/> is <see cref="StorageType.ExternalUsb"/>).
800 public Task<string> CreateThumbnailAsync(string mediaId, CancellationToken cancellationToken)
804 return cancellationToken.IsCancellationRequested ? Task.FromCanceled<string>(cancellationToken) :
805 CreateThumbnailAsyncCore(mediaId, cancellationToken);
808 private async Task<string> CreateThumbnailAsyncCore(string mediaId, CancellationToken cancellationToken)
810 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
812 var tcs = new TaskCompletionSource<string>();
814 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to create thumbnail");
816 if (handle.IsInvalid)
818 throw new RecordNotFoundException("Media does not exist.");
823 if (InteropHelper.GetValue<StorageType>(handle, Interop.MediaInfo.GetStorageType) == StorageType.ExternalUsb)
825 throw new UnsupportedContentException("The media is in external usb storage.");
828 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
830 if (File.Exists(path) == false)
832 throw new FileNotFoundException($"The media file does not exist. Path={path}.", path);
835 using (RegisterCancelThumbnail(cancellationToken, tcs, handle))
836 using (var cbKeeper = ObjectKeeper.Get(GetCreateThumbnailCallback(tcs)))
838 Interop.MediaInfo.CreateThumbnail(handle, cbKeeper.Target).ThrowIfError("Failed to create thumbnail");
840 return await tcs.Task;
845 private static Interop.MediaInfo.ThumbnailCompletedCallback GetCreateThumbnailCallback(
846 TaskCompletionSource<string> tcs)
848 return (error, path, _) =>
850 if (error != MediaContentError.None)
852 tcs.TrySetException(error.AsException("Failed to create thumbnail"));
856 tcs.TrySetResult(path);
861 private static IDisposable RegisterCancelThumbnail(CancellationToken cancellationToken,
862 TaskCompletionSource<string> tcs, Interop.MediaInfoHandle handle)
864 if (cancellationToken.CanBeCanceled == false)
869 return cancellationToken.Register(() =>
871 if (tcs.Task.IsCompleted)
876 Interop.MediaInfo.CancelThumbnail(handle).ThrowIfError("Failed to cancel");
877 tcs.TrySetCanceled();
882 #region DetectFaceAsync
884 /// Detects faces from the given media.
885 /// If the thumbnail already exists for the given media, the existing path will be returned.
887 /// <privilege>http://tizen.org/privilege/content.write</privilege>
888 /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
889 /// <param name="mediaId">The media ID to create the thumbnail.</param>
890 /// <returns>A task that represents the asynchronous add operation. The task result contains the number of faces detected.</returns>
891 /// <exception cref="InvalidOperationException">
892 /// The <see cref="MediaDatabase"/> is disconnected.\n
894 /// An internal error occurred while executing.
896 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
897 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
898 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
899 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
900 /// <exception cref="ArgumentException">
901 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
903 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
904 /// <exception cref="UnsupportedContentException">Face detection is not available for the given media.</exception>
905 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
906 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
907 public Task<int> DetectFaceAsync(string mediaId)
909 return DetectFaceAsync(mediaId, CancellationToken.None);
913 /// Creates the thumbnail image for the given media.
914 /// If the thumbnail already exists for the given media, the existing path will be returned.
917 /// Media in the external storage is not supported, with the exception of MMC.
919 /// <privilege>http://tizen.org/privilege/content.write</privilege>
920 /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
921 /// <param name="mediaId">The media ID to create the thumbnail.</param>
922 /// <param name="cancellationToken">The token to cancel the operation.</param>
923 /// <returns>A task that represents the asynchronous operation. The task result contains the number of faces detected.</returns>
924 /// <exception cref="InvalidOperationException">
925 /// The <see cref="MediaDatabase"/> is disconnected.\n
927 /// An internal error occurred while executing.
929 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
930 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
931 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
932 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
933 /// <exception cref="ArgumentException">
934 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
936 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
937 /// <exception cref="UnsupportedContentException">
938 /// Face detection is not available for the given media.\n
940 /// The media is in the external USB storage (<see cref="MediaInfo.StorageType"/> is <see cref="StorageType.ExternalUsb"/>).
942 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
943 public Task<int> DetectFaceAsync(string mediaId, CancellationToken cancellationToken)
945 if (Features.IsSupported(Features.FaceRecognition) == false)
947 throw new NotSupportedException($"The feature({Features.FaceRecognition}) is not supported.");
952 return cancellationToken.IsCancellationRequested ? Task.FromCanceled<int>(cancellationToken) :
953 DetectFaceAsyncCore(mediaId, cancellationToken);
956 private static async Task<int> DetectFaceAsyncCore(string mediaId, CancellationToken cancellationToken)
958 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
960 var tcs = new TaskCompletionSource<int>();
962 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to detect faces");
964 if (handle.IsInvalid)
966 throw new RecordNotFoundException("Media does not exist.");
971 if (InteropHelper.GetValue<StorageType>(handle, Interop.MediaInfo.GetStorageType) == StorageType.ExternalUsb)
973 throw new UnsupportedContentException("The media is in external usb storage.");
976 if (InteropHelper.GetValue<MediaType>(handle, Interop.MediaInfo.GetMediaType) != MediaType.Image)
978 throw new UnsupportedContentException("Only image is supported.");
981 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
983 if (File.Exists(path) == false)
985 throw new FileNotFoundException($"The media file does not exist. Path={path}.", path);
988 using (RegisterCancelFaceDetection(cancellationToken, tcs, handle))
989 using (var cbKeeper = ObjectKeeper.Get(GetFaceDetectionCallback(tcs)))
991 Interop.MediaInfo.StartFaceDetection(handle, cbKeeper.Target).ThrowIfError("Failed to detect faces");
993 return await tcs.Task;
998 private static Interop.MediaInfo.FaceDetectionCompletedCallback GetFaceDetectionCallback(
999 TaskCompletionSource<int> tcs)
1001 return (error, count, _) =>
1003 if (error != MediaContentError.None)
1005 tcs.TrySetException(error.AsException("Failed to detect faces"));
1009 tcs.TrySetResult(count);
1014 private static IDisposable RegisterCancelFaceDetection(CancellationToken cancellationToken,
1015 TaskCompletionSource<int> tcs, Interop.MediaInfoHandle handle)
1017 if (cancellationToken.CanBeCanceled == false)
1022 return cancellationToken.Register(() =>
1024 if (tcs.Task.IsCompleted)
1029 Interop.MediaInfo.CancelFaceDetection(handle).ThrowIfError("Failed to cancel");
1030 tcs.TrySetCanceled();