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 public int CountFaceInfo(string mediaId, CountArguments arguments)
152 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
154 return CommandHelper.Count(Interop.MediaInfo.GetFaceCount, mediaId, arguments);
158 /// Retrieves the face information added to or detected from the media.
160 /// <param name="mediaId">The media ID to select face information added to the media.</param>
161 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
162 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
163 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
164 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
165 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
166 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
167 /// <since_tizen> 4 </since_tizen>
168 public MediaDataReader<FaceInfo> SelectFaceInfo(string mediaId)
170 return SelectFaceInfo(mediaId, null);
174 /// Retrieves the face information added to or detected from the media with the <see cref="SelectArguments"/>.
176 /// <param name="mediaId">The media ID to select the face information added to the media.</param>
177 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
178 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
179 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
180 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
181 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
182 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
183 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
184 /// <since_tizen> 4 </since_tizen>
185 public MediaDataReader<FaceInfo> SelectFaceInfo(string mediaId, SelectArguments arguments)
189 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
191 return CommandHelper.SelectMembers(mediaId, arguments, Interop.MediaInfo.ForeachFaces,
192 FaceInfo.FromHandle);
196 /// Retrieves the number of tags that the media has.
198 /// <returns>The number of tags.</returns>
199 /// <param name="mediaId">The media ID to count tags added to the media.</param>
200 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
201 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
202 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
203 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
204 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
205 /// <since_tizen> 4 </since_tizen>
206 public int CountTag(string mediaId)
208 return CountTag(mediaId, null);
212 /// Retrieves the number of tags that the media has with the <see cref="CountArguments"/>.
214 /// <param name="mediaId">The media ID to count tags added to the media.</param>
215 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
216 /// <returns>The number of tags.</returns>
217 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
218 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
219 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
220 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
221 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
222 /// <since_tizen> 4 </since_tizen>
223 public int CountTag(string mediaId, CountArguments arguments)
227 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
229 return CommandHelper.Count(Interop.MediaInfo.GetTagCount, mediaId, arguments);
233 /// Retrieves the tags that the media has.
235 /// <param name="mediaId">The media ID to select tags added to the media.</param>
236 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
237 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
238 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
239 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
240 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
241 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
242 /// <since_tizen> 4 </since_tizen>
243 public MediaDataReader<Tag> SelectTag(string mediaId)
245 return SelectTag(mediaId, null);
249 /// Retrieves the tags that the media has with the <see cref="SelectArguments"/>.
251 /// <param name="mediaId">The media ID to select tags added to the media.</param>
252 /// <param name="filter">The criteria to use to filter. This value can be null.</param>
253 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
254 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
255 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
256 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
257 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
258 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
259 /// <since_tizen> 4 </since_tizen>
260 public MediaDataReader<Tag> SelectTag(string mediaId, SelectArguments filter)
264 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
266 return CommandHelper.SelectMembers(mediaId, filter, Interop.MediaInfo.ForeachTags,
271 /// Retrieves the number of the media information.
273 /// <returns>The number of the media information.</returns>
274 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
275 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
276 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
277 /// <since_tizen> 4 </since_tizen>
278 public int CountMedia()
280 return CountMedia(null);
284 /// Retrieves the number of the media information with the <see cref="SelectArguments"/>.
286 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
287 /// <returns>The number of media information.</returns>
288 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
289 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
290 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
291 /// <since_tizen> 4 </since_tizen>
292 public int CountMedia(CountArguments arguments)
296 return CommandHelper.Count(Interop.MediaInfo.GetMediaCount, arguments);
300 /// Retrieves the media.
302 /// <param name="mediaId">The media ID to retrieve.</param>
303 /// <returns>The <see cref="MediaInfo"/> instance if the matched record was found in the database, otherwise null.</returns>
304 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
305 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
306 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
307 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
308 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
309 /// <since_tizen> 4 </since_tizen>
310 public MediaInfo SelectMedia(string mediaId)
314 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
316 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).Ignore(MediaContentError.InvalidParameter).
317 ThrowIfError("Failed to query");
321 return MediaInfo.FromHandle(handle);
330 /// Retrieves the number of values grouped by the specified column with the <see cref="SelectArguments"/>.
332 /// <param name="columnKey">The column key.</param>
333 /// <returns>The number of groups.</returns>
334 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
335 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
336 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
337 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
338 /// <since_tizen> 4 </since_tizen>
339 public int CountGroupBy(MediaInfoColumnKey columnKey)
341 return CountGroupBy(columnKey, null);
345 /// Retrieves the number of values grouped by the specified column with the <see cref="SelectArguments"/>.
347 /// <param name="columnKey">The column key.</param>
348 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
349 /// <returns>The number of groups.</returns>
350 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
351 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
352 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
353 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
354 /// <since_tizen> 4 </since_tizen>
355 public int CountGroupBy(MediaInfoColumnKey columnKey, CountArguments arguments)
359 ValidationUtil.ValidateEnum(typeof(MediaInfoColumnKey), columnKey, nameof(columnKey));
361 using (var filter = QueryArguments.ToNativeHandle(arguments))
363 Interop.Group.GetGroupCount(filter, columnKey, out var count).ThrowIfError("Failed to query count");
369 /// Retrieves the group values of the specified column.
371 /// <param name="columnKey">The column key.</param>
372 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
373 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
374 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
375 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
376 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
377 /// <since_tizen> 4 </since_tizen>
378 public MediaDataReader<string> SelectGroupBy(MediaInfoColumnKey columnKey)
380 return SelectGroupBy(columnKey, null);
384 /// Retrieves the group values of the specified column with the <see cref="SelectArguments"/>.
386 /// <param name="columnKey">The column key.</param>
387 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
388 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
389 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
390 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
391 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
392 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
393 /// <since_tizen> 4 </since_tizen>
394 public MediaDataReader<string> SelectGroupBy(MediaInfoColumnKey columnKey, SelectArguments arguments)
398 ValidationUtil.ValidateEnum(typeof(MediaInfoColumnKey), columnKey, nameof(columnKey));
400 List<string> list = new List<string>();
402 using (var filter = QueryArguments.ToNativeHandle(arguments))
404 Interop.Group.ForeachGroup(filter, columnKey, (name, _) =>
409 }).ThrowIfError("Failed to query");
411 return new MediaDataReader<string>(list);
416 /// Retrieves all the media.
418 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
419 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
420 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
421 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
422 /// <since_tizen> 4 </since_tizen>
423 public MediaDataReader<MediaInfo> SelectMedia()
425 return SelectMedia(arguments: null);
429 /// Retrieves the media with the <see cref="SelectArguments"/>.
431 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
432 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
433 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
434 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
435 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
436 /// <since_tizen> 4 </since_tizen>
437 public MediaDataReader<MediaInfo> SelectMedia(SelectArguments arguments)
441 return new MediaDataReader<MediaInfo>(QueryMedia(arguments));
444 private static List<MediaInfo> QueryMedia(SelectArguments arguments)
446 using (var filter = QueryArguments.ToNativeHandle(arguments))
448 List<MediaInfo> list = new List<MediaInfo>();
450 Exception caught = null;
452 Interop.MediaInfo.ForeachMedia(filter, (handle, _) =>
456 list.Add(MediaInfo.FromHandle(handle));
476 /// Retrieves all matched ebook paths with given <paramref name="keyword"/>.
478 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
479 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
480 /// <param name="keyword">The keyword to search.</param>
481 /// <returns>A list of ebook paths which contain <paramref name="keyword"/>.</returns>
482 /// <exception cref="ArgumentNullException"><paramref name="keyword"/> is null.</exception>
483 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
484 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
485 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
486 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
487 /// <since_tizen> 9 </since_tizen>
488 public MediaDataReader<string> SelectEbookPath(string keyword)
492 IntPtr path = IntPtr.Zero;
495 ValidationUtil.ValidateNotNullOrEmpty(keyword, nameof(keyword));
499 Interop.BookInfo.GetPathByKeyword(keyword, out path, out length).
500 ThrowIfError("Failed to get path by keyword");
502 var list = new List<string>();
504 for (int i = 0; i < length; i++)
506 list.Add(Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(current)));
507 current = (IntPtr)((long)current + Marshal.SizeOf(typeof(IntPtr)));
510 return new MediaDataReader<string>(list);
515 for (int i = 0; i < length; i++)
517 Interop.Libc.Free(Marshal.ReadIntPtr(current));
518 current = (IntPtr)((long)current + Marshal.SizeOf(typeof(IntPtr)));
524 /// Deletes the media from the database.
526 /// <privilege>http://tizen.org/privilege/content.write</privilege>
527 /// <param name="mediaId">The media ID to delete.</param>
528 /// <returns>true if the matched record was found and deleted, otherwise false.</returns>
530 /// The <see cref="MediaDatabase.ScanFile(string)"/> or the <see cref="MediaDatabase.ScanFolderAsync(string)"/> can be used instead.<br/>
531 /// Since API level 6, if the file related with the <paramref name="mediaId"/> in DB still exists in file system before calling this method,
532 /// <see cref="InvalidOperationException"/> will be thrown to keep consistency in DB.
534 /// <exception cref="InvalidOperationException">
535 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
537 /// The file related with the <paramref name="mediaId"/> in DB still exists in file system. (Since API level 6)
539 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
540 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
541 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
542 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
543 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
544 /// <since_tizen> 4 </since_tizen>
545 public bool Delete(string mediaId)
549 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
551 if (CommandHelper.Count(
552 Interop.MediaInfo.GetMediaCount, $"{MediaInfoColumns.Id}='{mediaId}'") == 0)
557 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).
558 ThrowIfError("Failed to delete MediaInfo.");
560 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
562 // If we don't check file existence before calling `ScanFile` method,
563 // The inconsistency between DB and file system could be occurred.
564 if (File.Exists(path))
566 throw new InvalidOperationException("File still exists in file system. Remove it first.");
569 // Native 'delete' function was deprecated, so we need to use 'scan file' function instead of it.
570 Database.ScanFile(path);
576 /// Adds the media to the database.
578 /// <param name="path">The file path to add.</param>
579 /// <returns>The <see cref="MediaInfo"/> instance that contains the record information in the database.</returns>
581 /// If the media already exists in the database, it returns the existing information.<br/>
583 /// The <see cref="MediaDatabase.ScanFile(string)"/> or the <see cref="MediaDatabase.ScanFolderAsync(string)"/> can be used instead.<br/>
585 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.<br/>
586 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.<br/>
588 /// If http://tizen.org/feature/content.scanning.others feature is not supported and the specified file is other-type,
589 /// <see cref="NotSupportedException"/> will be thrown.
591 /// <privilege>http://tizen.org/privilege/content.write</privilege>
592 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
593 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
594 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
595 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
596 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
597 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
598 /// <exception cref="ArgumentException">
599 /// <paramref name="path"/> is a zero-length string, contains only white space.<br/>
601 /// <paramref name="path"/> contains a hidden path that starts with '.'.<br/>
603 /// <paramref name="path"/> contains a directory containing the ".scan_ignore" file.
605 /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exists.</exception>
606 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
607 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
608 /// <since_tizen> 4 </since_tizen>
609 public MediaInfo Add(string path)
613 ValidationUtil.ValidateNotNullOrEmpty(path, nameof(path));
615 if (File.Exists(path) == false)
617 throw new FileNotFoundException("destination is not valid path.", path);
620 if (File.GetAttributes(path).HasFlag(FileAttributes.Hidden))
622 throw new ArgumentException($"{nameof(path)} contains a hidden path.", nameof(path));
625 Interop.MediaInfoHandle handle = null;
629 Interop.MediaInfo.Insert(path, out handle).ThrowIfError("Failed to insert");
631 return MediaInfo.FromHandle(handle);
642 private static void ValidatePaths(IEnumerable<string> paths)
646 throw new ArgumentNullException(nameof(paths));
649 if (paths.Count() > 300)
651 throw new ArgumentException("Too many paths to add.");
654 foreach (var path in paths)
658 throw new ArgumentException($"{nameof(paths)} contains null.", nameof(paths));
661 if (File.Exists(path) == false)
663 throw new FileNotFoundException($"{nameof(paths)} contains a path that does not exist. Path={path}.", path);
670 /// Adds media files into the media database.
673 /// The paths that already exist in the database will be ignored.<br/>
674 /// At most 300 items can be added at once.<br/>
676 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.<br/>
677 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.<br/>
679 /// If http://tizen.org/feature/content.scanning.others feature is not supported and the specified file is other-type,
680 /// <see cref="NotSupportedException"/> will be thrown.
682 /// <privilege>http://tizen.org/privilege/content.write</privilege>
683 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
684 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
685 /// <param name="paths">The paths of the media files to add.</param>
686 /// <returns>A task that represents the asynchronous add operation.</returns>
687 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
688 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
689 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
690 /// <exception cref="ArgumentNullException"><paramref name="paths"/> is null.</exception>
691 /// <exception cref="ArgumentException">
692 /// <paramref name="paths"/> contains null.<br/>
694 /// <paramref name="paths"/> contains the invalid path.<br/>
696 /// The number of <paramref name="paths"/> is 300 or more items.
698 /// <exception cref="FileNotFoundException"><paramref name="paths"/> contains a path that does not exist.</exception>
699 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
700 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
701 /// <since_tizen> 4 </since_tizen>
702 public async Task AddAsync(IEnumerable<string> paths)
706 ValidatePaths(paths);
708 var pathArray = paths.ToArray();
709 var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
711 Interop.MediaInfo.InsertCompletedCallback callback = (error, _) =>
713 if (error == MediaContentError.None)
715 tcs.TrySetResult(true);
719 tcs.TrySetException(error.AsException("Failed to add"));
723 using (ObjectKeeper.Get(callback))
725 Interop.MediaInfo.BatchInsert(pathArray, pathArray.Length, callback).ThrowIfError("Failed to add");
732 /// Updates the media with the favorite value.
734 /// <privilege>http://tizen.org/privilege/content.write</privilege>
735 /// <param name="mediaId">The media ID to update.</param>
736 /// <param name="value">The value indicating whether the media is favorite.</param>
737 /// <returns>true if the matched record was found and updated, otherwise false.</returns>
738 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
739 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
740 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
741 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
742 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
743 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
744 /// <since_tizen> 4 </since_tizen>
745 public bool UpdateFavorite(string mediaId, bool value)
749 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
751 if (CommandHelper.Count(
752 Interop.MediaInfo.GetMediaCount, $"{MediaInfoColumns.Id}='{mediaId}'") == 0)
757 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to update");
759 if (handle.IsInvalid)
766 Interop.MediaInfo.SetFavorite(handle, value).ThrowIfError("Failed to update");
768 Interop.MediaInfo.UpdateToDB(handle).ThrowIfError("Failed to update");
778 /// Updates the path of the media to the specified destination path in the database.
780 /// <privilege>http://tizen.org/privilege/content.write</privilege>
781 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
782 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
783 /// <param name="mediaId">The media ID to move.</param>
784 /// <param name="newPath">The path that the media has been moved to.</param>
785 /// <returns>true if the matched record was found and updated, otherwise false.</returns>
787 /// Usually, it is used after the media file is moved to the another path.<br/>
789 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.<br/>
790 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
792 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
793 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
794 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
795 /// <exception cref="ArgumentNullException">
796 /// <paramref name="mediaId"/> is null.<br/>
798 /// <paramref name="newPath"/> is null.
800 /// <exception cref="ArgumentException">
801 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.<br/>
803 /// <paramref name="newPath"/> is a zero-length string, contains only white space.<br/>
805 /// <paramref name="newPath"/> contains a hidden directory that starts with '.'.<br/>
807 /// <paramref name="newPath"/> contains a directory containing the ".scan_ignore" file.
809 /// <exception cref="FileNotFoundException"><paramref name="newPath"/> does not exists.</exception>
810 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
811 /// <since_tizen> 4 </since_tizen>
812 public bool Move(string mediaId, string newPath)
816 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
818 ValidationUtil.ValidateNotNullOrEmpty(newPath, nameof(newPath));
820 if (File.Exists(newPath) == false)
822 throw new FileNotFoundException("destination is not valid path.", newPath);
825 if (File.GetAttributes(newPath).HasFlag(FileAttributes.Hidden))
827 throw new ArgumentException($"{nameof(newPath)} contains a hidden path.", nameof(newPath));
830 //TODO can be improved if MoveToDB supports result value.
831 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).
832 Ignore(MediaContentError.InvalidParameter).ThrowIfError("Failed to move");
834 if (handle.IsInvalid)
841 Interop.MediaInfo.MoveToDB(handle, newPath).ThrowIfError("Failed to move");
851 #region CreateThumbnailAsync
853 /// Creates the thumbnail image for the given media.
854 /// If the thumbnail already exists for the given media, the existing path will be returned.
856 /// <privilege>http://tizen.org/privilege/content.write</privilege>
857 /// <param name="mediaId">The media ID to create the thumbnail.</param>
858 /// <returns>A task that represents the asynchronous operation. The task result contains the thumbnail path.</returns>
859 /// <exception cref="InvalidOperationException">
860 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
862 /// An internal error occurred while executing.
864 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
865 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
866 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
867 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
868 /// <exception cref="ArgumentException">
869 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
871 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
872 /// <exception cref="UnsupportedContentException">
873 /// The thumbnail is not available for the given media.<br/>
875 /// The media is in the external USB storage.
877 /// <since_tizen> 4 </since_tizen>
878 [Obsolete("Deprecated since API10; Will be removed in API12. Please use CreateThumbnail instead.")]
879 public Task<string> CreateThumbnailAsync(string mediaId)
881 return CreateThumbnailAsync(mediaId, CancellationToken.None);
885 /// Creates the thumbnail image for the given media.
886 /// If the thumbnail already exists for the given media, the existing path will be returned.
888 /// <privilege>http://tizen.org/privilege/content.write</privilege>
889 /// <param name="mediaId">The media ID to create the thumbnail.</param>
890 /// <param name="cancellationToken">The token to cancel the operation.</param>
891 /// <returns>A task that represents the asynchronous operation. The task result contains the thumbnail path.</returns>
892 /// <exception cref="InvalidOperationException">
893 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
895 /// An internal error occurred while executing.
897 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
898 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
899 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
900 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
901 /// <exception cref="ArgumentException">
902 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
904 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
905 /// <exception cref="UnsupportedContentException">
906 /// The thumbnail is not available for the given media.<br/>
908 /// The media is in the external USB storage.
910 /// <since_tizen> 4 </since_tizen>
911 [Obsolete("Deprecated since API10; Will be removed in API12. Please use CreateThumbnail instead.")]
912 public Task<string> CreateThumbnailAsync(string mediaId, CancellationToken cancellationToken)
916 return cancellationToken.IsCancellationRequested ? Task.FromCanceled<string>(cancellationToken) :
917 CreateThumbnailAsyncCore(mediaId, cancellationToken);
920 private async Task<string> CreateThumbnailAsyncCore(string mediaId, CancellationToken cancellationToken)
922 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
924 var tcs = new TaskCompletionSource<string>();
926 using (var handle = ValidateFile(mediaId))
928 string thumbnailPath = null;
929 MediaContentError ret = MediaContentError.None;
930 Task thumbTask = null;
932 if (cancellationToken.CanBeCanceled)
934 cancellationToken.Register(() =>
936 if (tcs.Task.IsCompleted)
941 tcs.TrySetCanceled();
945 thumbTask = Task.Factory.StartNew( () =>
947 ret = Interop.MediaInfo.GenerateThumbnail(handle);
949 if (ret != MediaContentError.None)
951 tcs.TrySetException(ret.AsException("Failed to create thumbnail"));
955 thumbnailPath = InteropHelper.GetString(handle, Interop.MediaInfo.GetThumbnailPath, true);
956 tcs.TrySetResult(thumbnailPath);
958 }, cancellationToken,
959 TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning,
960 TaskScheduler.Default);
962 return await tcs.Task;
968 /// Creates the thumbnail image for the given media.
969 /// If the thumbnail already exists for the given media, the existing path will be returned.
971 /// <privilege>http://tizen.org/privilege/content.write</privilege>
972 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
973 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
974 /// <param name="mediaId">The ID of the media for which the thumbnail will be created.</param>
975 /// <returns>A created thumbnail path.</returns>
976 /// <exception cref="InvalidOperationException">
977 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
979 /// An internal error occurred while executing.
981 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
982 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
983 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
984 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
985 /// <exception cref="ArgumentException">
986 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
988 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
989 /// <exception cref="UnsupportedContentException">
990 /// The thumbnail is not available for the given media.<br/>
992 /// The media is in the external USB storage.
994 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
995 /// <since_tizen> 10 </since_tizen>
996 public string CreateThumbnail(string mediaId)
1000 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
1002 using (var handle = ValidateFile(mediaId))
1004 Interop.MediaInfo.GenerateThumbnail(handle).ThrowIfError("Failed to create thumbnail");
1006 return InteropHelper.GetString(handle, Interop.MediaInfo.GetThumbnailPath, true);
1010 private Interop.MediaInfoHandle ValidateFile(string mediaId)
1012 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to create thumbnail");
1014 if (handle.IsInvalid)
1016 throw new RecordNotFoundException("Media does not exist.");
1021 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
1023 if (String.IsNullOrEmpty(path) || File.Exists(path) == false)
1025 throw new FileNotFoundException($"The media file does not exist. Path={path}.", path);
1028 foreach (var extendedInternal in StorageManager.Storages.Where(s => s.StorageType == StorageArea.ExtendedInternal))
1030 if (path.Contains(extendedInternal.RootDirectory))
1032 throw new UnsupportedContentException("The media is in external usb storage.");
1036 catch (Exception ex)
1045 #region DetectFaceAsync
1047 /// Detects faces from the given media.
1048 /// If the thumbnail already exists for the given media, the existing path will be returned.
1050 /// <privilege>http://tizen.org/privilege/content.write</privilege>
1051 /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
1052 /// <param name="mediaId">The media ID to create the thumbnail.</param>
1053 /// <returns>A task that represents the asynchronous add operation. The task result contains the number of faces detected.</returns>
1054 /// <exception cref="InvalidOperationException">
1055 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
1057 /// An internal error occurred while executing.
1059 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
1060 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
1061 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
1062 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
1063 /// <exception cref="ArgumentException">
1064 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
1066 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
1067 /// <exception cref="UnsupportedContentException">Face detection is not available for the given media.</exception>
1068 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
1069 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
1070 /// <since_tizen> 4 </since_tizen>
1071 public Task<int> DetectFaceAsync(string mediaId)
1073 return DetectFaceAsync(mediaId, CancellationToken.None);
1077 /// Creates the thumbnail image for the given media.
1078 /// If the thumbnail already exists for the given media, the existing path will be returned.
1081 /// Media in the external storage is not supported, with the exception of MMC.
1082 /// Only JPEG, PNG, BMP images are supported.
1084 /// <privilege>http://tizen.org/privilege/content.write</privilege>
1085 /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
1086 /// <param name="mediaId">The media ID to create the thumbnail.</param>
1087 /// <param name="cancellationToken">The token to cancel the operation.</param>
1088 /// <returns>A task that represents the asynchronous operation. The task result contains the number of faces detected.</returns>
1089 /// <exception cref="InvalidOperationException">
1090 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
1092 /// An internal error occurred while executing.
1094 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
1095 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
1096 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
1097 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
1098 /// <exception cref="ArgumentException">
1099 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
1101 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
1102 /// <exception cref="UnsupportedContentException">
1103 /// Face detection is not available for the given media.<br/>
1105 /// The media is in the external USB storage.
1107 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
1108 /// <since_tizen> 4 </since_tizen>
1109 public Task<int> DetectFaceAsync(string mediaId, CancellationToken cancellationToken)
1111 if (Features.IsSupported(Features.FaceRecognition) == false)
1113 throw new NotSupportedException($"The feature({Features.FaceRecognition}) is not supported.");
1118 return cancellationToken.IsCancellationRequested ? Task.FromCanceled<int>(cancellationToken) :
1119 DetectFaceAsyncCore(mediaId, cancellationToken);
1122 private static async Task<int> DetectFaceAsyncCore(string mediaId, CancellationToken cancellationToken)
1124 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
1126 var tcs = new TaskCompletionSource<int>();
1128 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to detect faces");
1130 if (handle.IsInvalid)
1132 throw new RecordNotFoundException("Media does not exist.");
1137 if (InteropHelper.GetValue<MediaType>(handle, Interop.MediaInfo.GetMediaType) != MediaType.Image)
1139 throw new UnsupportedContentException("Only image is supported.");
1142 // Native P/Invoke function also check below case, but it returns invalid operation error.
1143 // So we check it here to throw more proper exception.
1144 string mimeType = InteropHelper.GetString(handle, Interop.MediaInfo.GetMimeType);
1145 if (!mimeType.Equals("image/jpeg") && !mimeType.Equals("image/png") && !mimeType.Equals("image/bmp"))
1147 throw new UnsupportedContentException($"{mimeType} is not supported. Only JPEG, PNG, BMP is supported.");
1150 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
1152 if (File.Exists(path) == false)
1154 throw new FileNotFoundException($"The media file does not exist. Path={path}.", path);
1157 using (RegisterCancelFaceDetection(cancellationToken, tcs, handle))
1158 using (var cbKeeper = ObjectKeeper.Get(GetFaceDetectionCallback(tcs)))
1160 var ret = Interop.MediaInfo.StartFaceDetection(handle, cbKeeper.Target);
1161 if (ret == MediaContentError.InvalidParameter)
1163 throw new UnsupportedContentException("The media is in external usb storage.");
1166 ret.ThrowIfError("Failed to detect faces");
1168 return await tcs.Task;
1173 private static Interop.MediaInfo.FaceDetectionCompletedCallback GetFaceDetectionCallback(
1174 TaskCompletionSource<int> tcs)
1176 return (error, count, _) =>
1178 if (error != MediaContentError.None)
1180 tcs.TrySetException(error.AsException("Failed to detect faces"));
1184 tcs.TrySetResult(count);
1189 private static IDisposable RegisterCancelFaceDetection(CancellationToken cancellationToken,
1190 TaskCompletionSource<int> tcs, Interop.MediaInfoHandle handle)
1192 if (cancellationToken.CanBeCanceled == false)
1197 return cancellationToken.Register(() =>
1199 if (tcs.Task.IsCompleted)
1204 Interop.MediaInfo.CancelFaceDetection(handle).ThrowIfError("Failed to cancel");
1205 tcs.TrySetCanceled();