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.Runtime.InteropServices;
22 using System.Threading;
23 using System.Threading.Tasks;
26 namespace Tizen.Content.MediaContent
29 /// Provides commands to manage the media information and query related items in the database.
31 /// <since_tizen> 4 </since_tizen>
32 public class MediaInfoCommand : MediaCommand
35 /// Initializes a new instance of the <see cref="FolderCommand"/> class with the specified <see cref="MediaDatabase"/>.
37 /// <param name="database">The <see cref="MediaDatabase"/> that the commands run on.</param>
38 /// <exception cref="ArgumentNullException"><paramref name="database"/> is null.</exception>
39 /// <exception cref="ObjectDisposedException"><paramref name="database"/> has already been disposed.</exception>
40 /// <since_tizen> 4 </since_tizen>
41 public MediaInfoCommand(MediaDatabase database) : base(database)
46 /// Retrieves the number of the bookmarks added to the media.
48 /// <param name="mediaId">The media ID to count the bookmarks added to the media.</param>
49 /// <returns>The number of the bookmarks.</returns>
50 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
51 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
52 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
53 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
54 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
55 /// <since_tizen> 4 </since_tizen>
56 public int CountBookmark(string mediaId)
58 return CountBookmark(mediaId, null);
62 /// Retrieves the number of the bookmarks added to the media with the <see cref="CountArguments"/>.
64 /// <param name="mediaId">The media ID to count the bookmarks added to the media.</param>
65 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
66 /// <returns>The number of the bookmarks.</returns>
67 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
68 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
69 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
70 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
71 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
72 /// <since_tizen> 4 </since_tizen>
73 public int CountBookmark(string mediaId, CountArguments arguments)
77 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
79 return CommandHelper.Count(Interop.MediaInfo.GetBookmarkCount, mediaId, arguments);
83 /// Retrieves the bookmarks added to the media.
85 /// <param name="mediaId">The media ID to select the bookmarks added to the media.</param>
86 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
87 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
88 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
89 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
90 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
91 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
92 /// <since_tizen> 4 </since_tizen>
93 public MediaDataReader<Bookmark> SelectBookmark(string mediaId)
95 return SelectBookmark(mediaId, null);
99 /// Retrieves the bookmarks added to the media with the <see cref="SelectArguments"/>.
101 /// <param name="mediaId">The media ID to select the bookmarks added to the media.</param>
102 /// <param name="filter">The criteria to use to filter. This value can be null.</param>
103 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
104 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
105 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
106 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
107 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
108 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
109 /// <since_tizen> 4 </since_tizen>
110 public MediaDataReader<Bookmark> SelectBookmark(string mediaId, SelectArguments filter)
114 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
116 return CommandHelper.SelectMembers(mediaId, filter, Interop.MediaInfo.ForeachBookmarks,
117 Bookmark.FromHandle);
121 /// Retrieves the number of the face information added to or detected from the media.
123 /// <param name="mediaId">The media ID to count face information added to the media.</param>
124 /// <returns>The number of the face information.</returns>
125 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
126 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
127 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
128 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
129 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
130 /// <since_tizen> 4 </since_tizen>
131 public int CountFaceInfo(string mediaId)
133 return CountFaceInfo(mediaId, null);
137 /// Retrieves the number of the face information added to or detected from the media with filter.
139 /// <param name="mediaId">The media ID to count the face information added to the media.</param>
140 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
141 /// <returns>The number of the face information.</returns>
142 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
143 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
144 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
145 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
146 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
147 /// <since_tizen> 4 </since_tizen>
148 [Obsolete("Deprecated since API11; Will be removed in API13.")]
149 public int CountFaceInfo(string mediaId, CountArguments arguments)
153 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
155 return CommandHelper.Count(Interop.MediaInfo.GetFaceCount, mediaId, arguments);
159 /// Retrieves the face information added to or detected from the media.
161 /// <param name="mediaId">The media ID to select face information added to the media.</param>
162 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
163 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
164 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
165 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
166 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
167 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
168 /// <since_tizen> 4 </since_tizen>
169 [Obsolete("Deprecated since API11; Will be removed in API13.")]
170 public MediaDataReader<FaceInfo> SelectFaceInfo(string mediaId)
172 return SelectFaceInfo(mediaId, null);
176 /// Retrieves the face information added to or detected from the media with the <see cref="SelectArguments"/>.
178 /// <param name="mediaId">The media ID to select the face information added to the media.</param>
179 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
180 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
181 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
182 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
183 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
184 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
185 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
186 /// <since_tizen> 4 </since_tizen>
187 [Obsolete("Deprecated since API11; Will be removed in API13.")]
188 public MediaDataReader<FaceInfo> SelectFaceInfo(string mediaId, SelectArguments arguments)
192 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
194 return CommandHelper.SelectMembers(mediaId, arguments, Interop.MediaInfo.ForeachFaces,
195 FaceInfo.FromHandle);
199 /// Retrieves the number of tags that the media has.
201 /// <returns>The number of tags.</returns>
202 /// <param name="mediaId">The media ID to count tags added to the media.</param>
203 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
204 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
205 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
206 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
207 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
208 /// <since_tizen> 4 </since_tizen>
209 public int CountTag(string mediaId)
211 return CountTag(mediaId, null);
215 /// Retrieves the number of tags that the media has with the <see cref="CountArguments"/>.
217 /// <param name="mediaId">The media ID to count tags added to the media.</param>
218 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
219 /// <returns>The number of tags.</returns>
220 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
221 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
222 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
223 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
224 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
225 /// <since_tizen> 4 </since_tizen>
226 public int CountTag(string mediaId, CountArguments arguments)
230 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
232 return CommandHelper.Count(Interop.MediaInfo.GetTagCount, mediaId, arguments);
236 /// Retrieves the tags that the media has.
238 /// <param name="mediaId">The media ID to select tags added to the media.</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.</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 /// <since_tizen> 4 </since_tizen>
246 public MediaDataReader<Tag> SelectTag(string mediaId)
248 return SelectTag(mediaId, null);
252 /// Retrieves the tags that the media has with the <see cref="SelectArguments"/>.
254 /// <param name="mediaId">The media ID to select tags added to the media.</param>
255 /// <param name="filter">The criteria to use to filter. This value can be null.</param>
256 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
257 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
258 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
259 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
260 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
261 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
262 /// <since_tizen> 4 </since_tizen>
263 public MediaDataReader<Tag> SelectTag(string mediaId, SelectArguments filter)
267 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
269 return CommandHelper.SelectMembers(mediaId, filter, Interop.MediaInfo.ForeachTags,
274 /// Retrieves the number of the media information.
276 /// <returns>The number of the media information.</returns>
277 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
278 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
279 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
280 /// <since_tizen> 4 </since_tizen>
281 public int CountMedia()
283 return CountMedia(null);
287 /// Retrieves the number of the media information with the <see cref="SelectArguments"/>.
289 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
290 /// <returns>The number of media information.</returns>
291 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
292 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
293 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
294 /// <since_tizen> 4 </since_tizen>
295 public int CountMedia(CountArguments arguments)
299 return CommandHelper.Count(Interop.MediaInfo.GetMediaCount, arguments);
303 /// Retrieves the media.
305 /// <param name="mediaId">The media ID to retrieve.</param>
306 /// <returns>The <see cref="MediaInfo"/> instance if the matched record was found in the database, otherwise null.</returns>
307 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
308 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
309 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
310 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
311 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
312 /// <since_tizen> 4 </since_tizen>
313 public MediaInfo SelectMedia(string mediaId)
317 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
319 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).Ignore(MediaContentError.InvalidParameter).
320 ThrowIfError("Failed to query");
324 return MediaInfo.FromHandle(handle);
333 /// Retrieves the number of values grouped by the specified column with the <see cref="SelectArguments"/>.
335 /// <param name="columnKey">The column key.</param>
336 /// <returns>The number of groups.</returns>
337 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
338 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
339 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
340 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
341 /// <since_tizen> 4 </since_tizen>
342 public int CountGroupBy(MediaInfoColumnKey columnKey)
344 return CountGroupBy(columnKey, null);
348 /// Retrieves the number of values grouped by the specified column with the <see cref="SelectArguments"/>.
350 /// <param name="columnKey">The column key.</param>
351 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
352 /// <returns>The number of groups.</returns>
353 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
354 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
355 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
356 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
357 /// <since_tizen> 4 </since_tizen>
358 public int CountGroupBy(MediaInfoColumnKey columnKey, CountArguments arguments)
362 ValidationUtil.ValidateEnum(typeof(MediaInfoColumnKey), columnKey, nameof(columnKey));
364 using (var filter = QueryArguments.ToNativeHandle(arguments))
366 Interop.Group.GetGroupCount(filter, columnKey, out var count).ThrowIfError("Failed to query count");
372 /// Retrieves the group values of the specified column.
374 /// <param name="columnKey">The column key.</param>
375 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
376 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
377 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
378 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
379 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
380 /// <since_tizen> 4 </since_tizen>
381 public MediaDataReader<string> SelectGroupBy(MediaInfoColumnKey columnKey)
383 return SelectGroupBy(columnKey, null);
387 /// Retrieves the group values of the specified column with the <see cref="SelectArguments"/>.
389 /// <param name="columnKey">The column key.</param>
390 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
391 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
392 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
393 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
394 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
395 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
396 /// <since_tizen> 4 </since_tizen>
397 public MediaDataReader<string> SelectGroupBy(MediaInfoColumnKey columnKey, SelectArguments arguments)
401 ValidationUtil.ValidateEnum(typeof(MediaInfoColumnKey), columnKey, nameof(columnKey));
403 List<string> list = new List<string>();
405 using (var filter = QueryArguments.ToNativeHandle(arguments))
407 Interop.Group.ForeachGroup(filter, columnKey, (name, _) =>
412 }).ThrowIfError("Failed to query");
414 return new MediaDataReader<string>(list);
419 /// Retrieves all the media.
421 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
422 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
423 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
424 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
425 /// <since_tizen> 4 </since_tizen>
426 public MediaDataReader<MediaInfo> SelectMedia()
428 return SelectMedia(arguments: null);
432 /// Retrieves the media with the <see cref="SelectArguments"/>.
434 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
435 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
436 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
437 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
438 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
439 /// <since_tizen> 4 </since_tizen>
440 public MediaDataReader<MediaInfo> SelectMedia(SelectArguments arguments)
444 return new MediaDataReader<MediaInfo>(QueryMedia(arguments));
447 private static List<MediaInfo> QueryMedia(SelectArguments arguments)
449 using (var filter = QueryArguments.ToNativeHandle(arguments))
451 List<MediaInfo> list = new List<MediaInfo>();
453 Exception caught = null;
455 Interop.MediaInfo.ForeachMedia(filter, (handle, _) =>
459 list.Add(MediaInfo.FromHandle(handle));
479 /// Retrieves all matched ebook paths with given <paramref name="keyword"/>.
481 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
482 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
483 /// <param name="keyword">The keyword to search.</param>
484 /// <returns>A list of ebook paths which contain <paramref name="keyword"/>.</returns>
485 /// <exception cref="ArgumentNullException"><paramref name="keyword"/> is null.</exception>
486 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
487 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
488 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
489 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
490 /// <since_tizen> 9 </since_tizen>
491 public MediaDataReader<string> SelectEbookPath(string keyword)
495 IntPtr path = IntPtr.Zero;
498 ValidationUtil.ValidateNotNullOrEmpty(keyword, nameof(keyword));
502 Interop.BookInfo.GetPathByKeyword(keyword, out path, out length).
503 ThrowIfError("Failed to get path by keyword");
505 var list = new List<string>();
507 for (int i = 0; i < length; i++)
509 list.Add(Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(current)));
510 current = (IntPtr)((long)current + Marshal.SizeOf(typeof(IntPtr)));
513 return new MediaDataReader<string>(list);
518 for (int i = 0; i < length; i++)
520 Marshal.FreeHGlobal(current);
521 current = (IntPtr)((long)current + Marshal.SizeOf(typeof(IntPtr)));
527 /// Deletes the media from the database.
529 /// <privilege>http://tizen.org/privilege/content.write</privilege>
530 /// <param name="mediaId">The media ID to delete.</param>
531 /// <returns>true if the matched record was found and deleted, otherwise false.</returns>
533 /// The <see cref="MediaDatabase.ScanFile(string)"/> or the <see cref="MediaDatabase.ScanFolderAsync(string)"/> can be used instead.<br/>
534 /// Since API level 6, if the file related with the <paramref name="mediaId"/> in DB still exists in file system before calling this method,
535 /// <see cref="InvalidOperationException"/> will be thrown to keep consistency in DB.
537 /// <exception cref="InvalidOperationException">
538 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
540 /// The file related with the <paramref name="mediaId"/> in DB still exists in file system. (Since API level 6)
542 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
543 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
544 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
545 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
546 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
547 /// <since_tizen> 4 </since_tizen>
548 public bool Delete(string mediaId)
552 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
554 if (CommandHelper.Count(
555 Interop.MediaInfo.GetMediaCount, $"{MediaInfoColumns.Id}='{mediaId}'") == 0)
560 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).
561 ThrowIfError("Failed to delete MediaInfo.");
563 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
565 // If we don't check file existence before calling `ScanFile` method,
566 // The inconsistency between DB and file system could be occurred.
567 if (File.Exists(path))
569 throw new InvalidOperationException("File still exists in file system. Remove it first.");
572 // Native 'delete' function was deprecated, so we need to use 'scan file' function instead of it.
573 Database.ScanFile(path);
579 /// Adds the media to the database.
581 /// <param name="path">The file path to add.</param>
582 /// <returns>The <see cref="MediaInfo"/> instance that contains the record information in the database.</returns>
584 /// If the media already exists in the database, it returns the existing information.<br/>
586 /// The <see cref="MediaDatabase.ScanFile(string)"/> or the <see cref="MediaDatabase.ScanFolderAsync(string)"/> can be used instead.<br/>
588 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.<br/>
589 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.<br/>
591 /// If http://tizen.org/feature/content.scanning.others feature is not supported and the specified file is other-type,
592 /// <see cref="NotSupportedException"/> will be thrown.
594 /// <privilege>http://tizen.org/privilege/content.write</privilege>
595 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
596 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
597 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
598 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
599 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
600 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
601 /// <exception cref="ArgumentException">
602 /// <paramref name="path"/> is a zero-length string, contains only white space.<br/>
604 /// <paramref name="path"/> contains a hidden path that starts with '.'.<br/>
606 /// <paramref name="path"/> contains a directory containing the ".scan_ignore" file.
608 /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exists.</exception>
609 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
610 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
611 /// <since_tizen> 4 </since_tizen>
612 public MediaInfo Add(string path)
616 ValidationUtil.ValidateNotNullOrEmpty(path, nameof(path));
618 if (File.Exists(path) == false)
620 throw new FileNotFoundException("destination is not valid path.", path);
623 if (File.GetAttributes(path).HasFlag(FileAttributes.Hidden))
625 throw new ArgumentException($"{nameof(path)} contains a hidden path.", nameof(path));
628 Interop.MediaInfoHandle handle = null;
632 Interop.MediaInfo.Insert(path, out handle).ThrowIfError("Failed to insert");
634 return MediaInfo.FromHandle(handle);
645 private static void ValidatePaths(IEnumerable<string> paths)
649 throw new ArgumentNullException(nameof(paths));
652 if (paths.Count() > 300)
654 throw new ArgumentException("Too many paths to add.");
657 foreach (var path in paths)
661 throw new ArgumentException($"{nameof(paths)} contains null.", nameof(paths));
664 if (File.Exists(path) == false)
666 throw new FileNotFoundException($"{nameof(paths)} contains a path that does not exist. Path={path}.", path);
673 /// Adds media files into the media database.
676 /// The paths that already exist in the database will be ignored.<br/>
677 /// At most 300 items can be added at once.<br/>
679 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.<br/>
680 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.<br/>
682 /// If http://tizen.org/feature/content.scanning.others feature is not supported and the specified file is other-type,
683 /// <see cref="NotSupportedException"/> will be thrown.
685 /// <privilege>http://tizen.org/privilege/content.write</privilege>
686 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
687 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
688 /// <param name="paths">The paths of the media files to add.</param>
689 /// <returns>A task that represents the asynchronous add operation.</returns>
690 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
691 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
692 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
693 /// <exception cref="ArgumentNullException"><paramref name="paths"/> is null.</exception>
694 /// <exception cref="ArgumentException">
695 /// <paramref name="paths"/> contains null.<br/>
697 /// <paramref name="paths"/> contains the invalid path.<br/>
699 /// The number of <paramref name="paths"/> is 300 or more items.
701 /// <exception cref="FileNotFoundException"><paramref name="paths"/> contains a path that does not exist.</exception>
702 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
703 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
704 /// <since_tizen> 4 </since_tizen>
705 public async Task AddAsync(IEnumerable<string> paths)
709 ValidatePaths(paths);
711 var pathArray = paths.ToArray();
712 var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
714 Interop.MediaInfo.InsertCompletedCallback callback = (error, _) =>
716 if (error == MediaContentError.None)
718 tcs.TrySetResult(true);
722 tcs.TrySetException(error.AsException("Failed to add"));
726 using (ObjectKeeper.Get(callback))
728 Interop.MediaInfo.BatchInsert(pathArray, pathArray.Length, callback).ThrowIfError("Failed to add");
735 /// Updates the media with the favorite value.
737 /// <privilege>http://tizen.org/privilege/content.write</privilege>
738 /// <param name="mediaId">The media ID to update.</param>
739 /// <param name="value">The value indicating whether the media is favorite.</param>
740 /// <returns>true if the matched record was found and updated, otherwise false.</returns>
741 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
742 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
743 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
744 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
745 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
746 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
747 /// <since_tizen> 4 </since_tizen>
748 public bool UpdateFavorite(string mediaId, bool value)
752 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
754 if (CommandHelper.Count(
755 Interop.MediaInfo.GetMediaCount, $"{MediaInfoColumns.Id}='{mediaId}'") == 0)
760 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to update");
762 if (handle.IsInvalid)
769 Interop.MediaInfo.SetFavorite(handle, value).ThrowIfError("Failed to update");
771 Interop.MediaInfo.UpdateToDB(handle).ThrowIfError("Failed to update");
781 /// Updates the path of the media to the specified destination path in the database.
783 /// <privilege>http://tizen.org/privilege/content.write</privilege>
784 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
785 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
786 /// <param name="mediaId">The media ID to move.</param>
787 /// <param name="newPath">The path that the media has been moved to.</param>
788 /// <returns>true if the matched record was found and updated, otherwise false.</returns>
790 /// Usually, it is used after the media file is moved to the another path.<br/>
792 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.<br/>
793 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
795 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
796 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
797 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
798 /// <exception cref="ArgumentNullException">
799 /// <paramref name="mediaId"/> is null.<br/>
801 /// <paramref name="newPath"/> is null.
803 /// <exception cref="ArgumentException">
804 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.<br/>
806 /// <paramref name="newPath"/> is a zero-length string, contains only white space.<br/>
808 /// <paramref name="newPath"/> contains a hidden directory that starts with '.'.<br/>
810 /// <paramref name="newPath"/> contains a directory containing the ".scan_ignore" file.
812 /// <exception cref="FileNotFoundException"><paramref name="newPath"/> does not exists.</exception>
813 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
814 /// <since_tizen> 4 </since_tizen>
815 public bool Move(string mediaId, string newPath)
819 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
821 ValidationUtil.ValidateNotNullOrEmpty(newPath, nameof(newPath));
823 if (File.Exists(newPath) == false)
825 throw new FileNotFoundException("destination is not valid path.", newPath);
828 if (File.GetAttributes(newPath).HasFlag(FileAttributes.Hidden))
830 throw new ArgumentException($"{nameof(newPath)} contains a hidden path.", nameof(newPath));
833 //TODO can be improved if MoveToDB supports result value.
834 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).
835 Ignore(MediaContentError.InvalidParameter).ThrowIfError("Failed to move");
837 if (handle.IsInvalid)
844 Interop.MediaInfo.MoveToDB(handle, newPath).ThrowIfError("Failed to move");
854 #region CreateThumbnailAsync
856 /// Creates the thumbnail image for the given media.
857 /// If the thumbnail already exists for the given media, the existing path will be returned.
859 /// <privilege>http://tizen.org/privilege/content.write</privilege>
860 /// <param name="mediaId">The media ID to create the thumbnail.</param>
861 /// <returns>A task that represents the asynchronous operation. The task result contains the thumbnail path.</returns>
862 /// <exception cref="InvalidOperationException">
863 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
865 /// An internal error occurred while executing.
867 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
868 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
869 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
870 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
871 /// <exception cref="ArgumentException">
872 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
874 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
875 /// <exception cref="UnsupportedContentException">
876 /// The thumbnail is not available for the given media.<br/>
878 /// The media is in the external USB storage.
880 /// <since_tizen> 4 </since_tizen>
881 [Obsolete("Deprecated since API10; Will be removed in API12. Please use CreateThumbnail instead.")]
882 public Task<string> CreateThumbnailAsync(string mediaId)
884 return CreateThumbnailAsync(mediaId, CancellationToken.None);
888 /// Creates the thumbnail image for the given media.
889 /// If the thumbnail already exists for the given media, the existing path will be returned.
891 /// <privilege>http://tizen.org/privilege/content.write</privilege>
892 /// <param name="mediaId">The media ID to create the thumbnail.</param>
893 /// <param name="cancellationToken">The token to cancel the operation.</param>
894 /// <returns>A task that represents the asynchronous operation. The task result contains the thumbnail path.</returns>
895 /// <exception cref="InvalidOperationException">
896 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
898 /// An internal error occurred while executing.
900 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
901 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
902 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
903 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
904 /// <exception cref="ArgumentException">
905 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
907 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
908 /// <exception cref="UnsupportedContentException">
909 /// The thumbnail is not available for the given media.<br/>
911 /// The media is in the external USB storage.
913 /// <since_tizen> 4 </since_tizen>
914 [Obsolete("Deprecated since API10; Will be removed in API12. Please use CreateThumbnail instead.")]
915 public Task<string> CreateThumbnailAsync(string mediaId, CancellationToken cancellationToken)
919 return cancellationToken.IsCancellationRequested ? Task.FromCanceled<string>(cancellationToken) :
920 CreateThumbnailAsyncCore(mediaId, cancellationToken);
923 private async Task<string> CreateThumbnailAsyncCore(string mediaId, CancellationToken cancellationToken)
925 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
927 var tcs = new TaskCompletionSource<string>();
929 using (var handle = ValidateFile(mediaId))
931 string thumbnailPath = null;
932 MediaContentError ret = MediaContentError.None;
933 Task thumbTask = null;
935 if (cancellationToken.CanBeCanceled)
937 cancellationToken.Register(() =>
939 if (tcs.Task.IsCompleted)
944 tcs.TrySetCanceled();
948 thumbTask = Task.Factory.StartNew( () =>
950 ret = Interop.MediaInfo.GenerateThumbnail(handle);
952 if (ret != MediaContentError.None)
954 tcs.TrySetException(ret.AsException("Failed to create thumbnail"));
958 thumbnailPath = InteropHelper.GetString(handle, Interop.MediaInfo.GetThumbnailPath, true);
959 tcs.TrySetResult(thumbnailPath);
961 }, cancellationToken,
962 TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning,
963 TaskScheduler.Default);
965 return await tcs.Task;
971 /// Creates the thumbnail image for the given media.
972 /// If the thumbnail already exists for the given media, the existing path will be returned.
974 /// <privilege>http://tizen.org/privilege/content.write</privilege>
975 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
976 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
977 /// <param name="mediaId">The ID of the media for which the thumbnail will be created.</param>
978 /// <returns>A created thumbnail path.</returns>
979 /// <exception cref="InvalidOperationException">
980 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
982 /// An internal error occurred while executing.
984 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
985 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
986 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
987 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
988 /// <exception cref="ArgumentException">
989 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
991 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
992 /// <exception cref="UnsupportedContentException">
993 /// The thumbnail is not available for the given media.<br/>
995 /// The media is in the external USB storage.
997 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
998 /// <since_tizen> 10 </since_tizen>
999 public string CreateThumbnail(string mediaId)
1003 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
1005 using (var handle = ValidateFile(mediaId))
1007 Interop.MediaInfo.GenerateThumbnail(handle).ThrowIfError("Failed to create thumbnail");
1009 return InteropHelper.GetString(handle, Interop.MediaInfo.GetThumbnailPath, true);
1013 private Interop.MediaInfoHandle ValidateFile(string mediaId)
1015 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to create thumbnail");
1017 if (handle.IsInvalid)
1019 throw new RecordNotFoundException("Media does not exist.");
1024 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
1026 if (String.IsNullOrEmpty(path) || File.Exists(path) == false)
1028 throw new FileNotFoundException($"The media file does not exist. Path={path}.", path);
1031 foreach (var extendedInternal in StorageManager.Storages.Where(s => s.StorageType == StorageArea.ExtendedInternal))
1033 if (path.Contains(extendedInternal.RootDirectory))
1035 throw new UnsupportedContentException("The media is in external usb storage.");
1039 catch (Exception ex)
1048 #region DetectFaceAsync
1050 /// Detects faces from the given media.
1051 /// If the thumbnail already exists for the given media, the existing path will be returned.
1053 /// <privilege>http://tizen.org/privilege/content.write</privilege>
1054 /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
1055 /// <param name="mediaId">The media ID to create the thumbnail.</param>
1056 /// <returns>A task that represents the asynchronous add operation. The task result contains the number of faces detected.</returns>
1057 /// <exception cref="InvalidOperationException">
1058 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
1060 /// An internal error occurred while executing.
1062 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
1063 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
1064 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
1065 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
1066 /// <exception cref="ArgumentException">
1067 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
1069 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
1070 /// <exception cref="UnsupportedContentException">Face detection is not available for the given media.</exception>
1071 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
1072 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
1073 /// <since_tizen> 4 </since_tizen>
1074 [Obsolete("Deprecated since API11; Will be removed in API13.")]
1075 public Task<int> DetectFaceAsync(string mediaId)
1077 return DetectFaceAsync(mediaId, CancellationToken.None);
1081 /// Creates the thumbnail image for the given media.
1082 /// If the thumbnail already exists for the given media, the existing path will be returned.
1085 /// Media in the external storage is not supported, with the exception of MMC.
1086 /// Only JPEG, PNG, BMP images are supported.
1088 /// <privilege>http://tizen.org/privilege/content.write</privilege>
1089 /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
1090 /// <param name="mediaId">The media ID to create the thumbnail.</param>
1091 /// <param name="cancellationToken">The token to cancel the operation.</param>
1092 /// <returns>A task that represents the asynchronous operation. The task result contains the number of faces detected.</returns>
1093 /// <exception cref="InvalidOperationException">
1094 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
1096 /// An internal error occurred while executing.
1098 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
1099 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
1100 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
1101 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
1102 /// <exception cref="ArgumentException">
1103 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
1105 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
1106 /// <exception cref="UnsupportedContentException">
1107 /// Face detection is not available for the given media.<br/>
1109 /// The media is in the external USB storage.
1111 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
1112 /// <since_tizen> 4 </since_tizen>
1113 [Obsolete("Deprecated since API11; Will be removed in API13.")]
1114 public Task<int> DetectFaceAsync(string mediaId, CancellationToken cancellationToken)
1116 if (Features.IsSupported(Features.FaceRecognition) == false)
1118 throw new NotSupportedException($"The feature({Features.FaceRecognition}) is not supported.");
1123 return cancellationToken.IsCancellationRequested ? Task.FromCanceled<int>(cancellationToken) :
1124 DetectFaceAsyncCore(mediaId, cancellationToken);
1127 private static async Task<int> DetectFaceAsyncCore(string mediaId, CancellationToken cancellationToken)
1129 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
1131 var tcs = new TaskCompletionSource<int>();
1133 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to detect faces");
1135 if (handle.IsInvalid)
1137 throw new RecordNotFoundException("Media does not exist.");
1142 if (InteropHelper.GetValue<MediaType>(handle, Interop.MediaInfo.GetMediaType) != MediaType.Image)
1144 throw new UnsupportedContentException("Only image is supported.");
1147 // Native P/Invoke function also check below case, but it returns invalid operation error.
1148 // So we check it here to throw more proper exception.
1149 string mimeType = InteropHelper.GetString(handle, Interop.MediaInfo.GetMimeType);
1150 if (!mimeType.Equals("image/jpeg") && !mimeType.Equals("image/png") && !mimeType.Equals("image/bmp"))
1152 throw new UnsupportedContentException($"{mimeType} is not supported. Only JPEG, PNG, BMP is supported.");
1155 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
1157 if (File.Exists(path) == false)
1159 throw new FileNotFoundException($"The media file does not exist. Path={path}.", path);
1162 using (RegisterCancelFaceDetection(cancellationToken, tcs, handle))
1163 using (var cbKeeper = ObjectKeeper.Get(GetFaceDetectionCallback(tcs)))
1165 var ret = Interop.MediaInfo.StartFaceDetection(handle, cbKeeper.Target);
1166 if (ret == MediaContentError.InvalidParameter)
1168 throw new UnsupportedContentException("The media is in external usb storage.");
1171 ret.ThrowIfError("Failed to detect faces");
1173 return await tcs.Task;
1178 private static Interop.MediaInfo.FaceDetectionCompletedCallback GetFaceDetectionCallback(
1179 TaskCompletionSource<int> tcs)
1181 return (error, count, _) =>
1183 if (error != MediaContentError.None)
1185 tcs.TrySetException(error.AsException("Failed to detect faces"));
1189 tcs.TrySetResult(count);
1194 private static IDisposable RegisterCancelFaceDetection(CancellationToken cancellationToken,
1195 TaskCompletionSource<int> tcs, Interop.MediaInfoHandle handle)
1197 if (cancellationToken.CanBeCanceled == false)
1202 return cancellationToken.Register(() =>
1204 if (tcs.Task.IsCompleted)
1209 Interop.MediaInfo.CancelFaceDetection(handle).ThrowIfError("Failed to cancel");
1210 tcs.TrySetCanceled();