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 [Obsolete("Deprecated since API12; Will be removed in API14.")]
227 public int CountTag(string mediaId, CountArguments arguments)
231 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
233 return CommandHelper.Count(Interop.MediaInfo.GetTagCount, mediaId, arguments);
237 /// Retrieves the tags that the media has.
239 /// <param name="mediaId">The media ID to select tags added to the media.</param>
240 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
241 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
242 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
243 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
244 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
245 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
246 /// <since_tizen> 4 </since_tizen>
247 [Obsolete("Deprecated since API12; Will be removed in API14.")]
248 public MediaDataReader<Tag> SelectTag(string mediaId)
250 return SelectTag(mediaId, null);
254 /// Retrieves the tags that the media has with the <see cref="SelectArguments"/>.
256 /// <param name="mediaId">The media ID to select tags added to the media.</param>
257 /// <param name="filter">The criteria to use to filter. This value can be null.</param>
258 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
259 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
260 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
261 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
262 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
263 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
264 /// <since_tizen> 4 </since_tizen>
265 [Obsolete("Deprecated since API12; Will be removed in API14.")]
266 public MediaDataReader<Tag> SelectTag(string mediaId, SelectArguments filter)
270 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
272 return CommandHelper.SelectMembers(mediaId, filter, Interop.MediaInfo.ForeachTags,
277 /// Retrieves the number of the media information.
279 /// <returns>The number of the media information.</returns>
280 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
281 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
282 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
283 /// <since_tizen> 4 </since_tizen>
284 public int CountMedia()
286 return CountMedia(null);
290 /// Retrieves the number of the media information with the <see cref="SelectArguments"/>.
292 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
293 /// <returns>The number of media information.</returns>
294 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
295 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
296 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
297 /// <since_tizen> 4 </since_tizen>
298 public int CountMedia(CountArguments arguments)
302 return CommandHelper.Count(Interop.MediaInfo.GetMediaCount, arguments);
306 /// Retrieves the media.
308 /// <param name="mediaId">The media ID to retrieve.</param>
309 /// <returns>The <see cref="MediaInfo"/> instance if the matched record was found in the database, otherwise null.</returns>
310 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
311 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
312 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
313 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
314 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
315 /// <since_tizen> 4 </since_tizen>
316 public MediaInfo SelectMedia(string mediaId)
320 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
322 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).Ignore(MediaContentError.InvalidParameter).
323 ThrowIfError("Failed to query");
327 return MediaInfo.FromHandle(handle);
336 /// Retrieves the number of values grouped by the specified column with the <see cref="SelectArguments"/>.
338 /// <param name="columnKey">The column key.</param>
339 /// <returns>The number of groups.</returns>
340 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
341 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
342 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
343 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
344 /// <since_tizen> 4 </since_tizen>
345 public int CountGroupBy(MediaInfoColumnKey columnKey)
347 return CountGroupBy(columnKey, null);
351 /// Retrieves the number of values grouped by the specified column with the <see cref="SelectArguments"/>.
353 /// <param name="columnKey">The column key.</param>
354 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
355 /// <returns>The number of groups.</returns>
356 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
357 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
358 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
359 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
360 /// <since_tizen> 4 </since_tizen>
361 public int CountGroupBy(MediaInfoColumnKey columnKey, CountArguments arguments)
365 ValidationUtil.ValidateEnum(typeof(MediaInfoColumnKey), columnKey, nameof(columnKey));
367 using (var filter = QueryArguments.ToNativeHandle(arguments))
369 Interop.Group.GetGroupCount(filter, columnKey, out var count).ThrowIfError("Failed to query count");
375 /// Retrieves the group values of the specified column.
377 /// <param name="columnKey">The column key.</param>
378 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
379 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
380 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
381 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
382 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
383 /// <since_tizen> 4 </since_tizen>
384 public MediaDataReader<string> SelectGroupBy(MediaInfoColumnKey columnKey)
386 return SelectGroupBy(columnKey, null);
390 /// Retrieves the group values of the specified column with the <see cref="SelectArguments"/>.
392 /// <param name="columnKey">The column key.</param>
393 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
394 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
395 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
396 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
397 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
398 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
399 /// <since_tizen> 4 </since_tizen>
400 public MediaDataReader<string> SelectGroupBy(MediaInfoColumnKey columnKey, SelectArguments arguments)
404 ValidationUtil.ValidateEnum(typeof(MediaInfoColumnKey), columnKey, nameof(columnKey));
406 List<string> list = new List<string>();
408 using (var filter = QueryArguments.ToNativeHandle(arguments))
410 Interop.Group.ForeachGroup(filter, columnKey, (name, _) =>
415 }).ThrowIfError("Failed to query");
417 return new MediaDataReader<string>(list);
422 /// Retrieves all the media.
424 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
425 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
426 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
427 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
428 /// <since_tizen> 4 </since_tizen>
429 public MediaDataReader<MediaInfo> SelectMedia()
431 return SelectMedia(arguments: null);
435 /// Retrieves the media with the <see cref="SelectArguments"/>.
437 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
438 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
439 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
440 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
441 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
442 /// <since_tizen> 4 </since_tizen>
443 public MediaDataReader<MediaInfo> SelectMedia(SelectArguments arguments)
447 return new MediaDataReader<MediaInfo>(QueryMedia(arguments));
450 private static List<MediaInfo> QueryMedia(SelectArguments arguments)
452 using (var filter = QueryArguments.ToNativeHandle(arguments))
454 List<MediaInfo> list = new List<MediaInfo>();
456 Exception caught = null;
458 Interop.MediaInfo.ForeachMedia(filter, (handle, _) =>
462 list.Add(MediaInfo.FromHandle(handle));
482 /// Retrieves all matched ebook paths with given <paramref name="keyword"/>.
484 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
485 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
486 /// <param name="keyword">The keyword to search.</param>
487 /// <returns>A list of ebook paths which contain <paramref name="keyword"/>.</returns>
488 /// <exception cref="ArgumentNullException"><paramref name="keyword"/> is null.</exception>
489 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
490 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
491 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
492 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
493 /// <since_tizen> 9 </since_tizen>
494 public MediaDataReader<string> SelectEbookPath(string keyword)
498 IntPtr path = IntPtr.Zero;
501 ValidationUtil.ValidateNotNullOrEmpty(keyword, nameof(keyword));
505 Interop.BookInfo.GetPathByKeyword(keyword, out path, out length).
506 ThrowIfError("Failed to get path by keyword");
508 var list = new List<string>();
510 for (int i = 0; i < length; i++)
512 list.Add(Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(current)));
513 current = (IntPtr)((long)current + Marshal.SizeOf(typeof(IntPtr)));
516 return new MediaDataReader<string>(list);
521 for (int i = 0; i < length; i++)
523 Marshal.FreeHGlobal(current);
524 current = (IntPtr)((long)current + Marshal.SizeOf(typeof(IntPtr)));
530 /// Deletes the media from the database.
532 /// <privilege>http://tizen.org/privilege/content.write</privilege>
533 /// <param name="mediaId">The media ID to delete.</param>
534 /// <returns>true if the matched record was found and deleted, otherwise false.</returns>
536 /// The <see cref="MediaDatabase.ScanFile(string)"/> or the <see cref="MediaDatabase.ScanFolderAsync(string)"/> can be used instead.<br/>
537 /// Since API level 6, if the file related with the <paramref name="mediaId"/> in DB still exists in file system before calling this method,
538 /// <see cref="InvalidOperationException"/> will be thrown to keep consistency in DB.
540 /// <exception cref="InvalidOperationException">
541 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
543 /// The file related with the <paramref name="mediaId"/> in DB still exists in file system. (Since API level 6)
545 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
546 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
547 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
548 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
549 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
550 /// <since_tizen> 4 </since_tizen>
551 public bool Delete(string mediaId)
555 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
557 if (CommandHelper.Count(
558 Interop.MediaInfo.GetMediaCount, $"{MediaInfoColumns.Id}='{mediaId}'") == 0)
563 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).
564 ThrowIfError("Failed to delete MediaInfo.");
566 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
568 // If we don't check file existence before calling `ScanFile` method,
569 // The inconsistency between DB and file system could be occurred.
570 if (File.Exists(path))
572 throw new InvalidOperationException("File still exists in file system. Remove it first.");
575 // Native 'delete' function was deprecated, so we need to use 'scan file' function instead of it.
576 Database.ScanFile(path);
582 /// Adds the media to the database.
584 /// <param name="path">The file path to add.</param>
585 /// <returns>The <see cref="MediaInfo"/> instance that contains the record information in the database.</returns>
587 /// If the media already exists in the database, it returns the existing information.<br/>
589 /// The <see cref="MediaDatabase.ScanFile(string)"/> or the <see cref="MediaDatabase.ScanFolderAsync(string)"/> can be used instead.<br/>
591 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.<br/>
592 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.<br/>
594 /// If http://tizen.org/feature/content.scanning.others feature is not supported and the specified file is other-type,
595 /// <see cref="NotSupportedException"/> will be thrown.
597 /// <privilege>http://tizen.org/privilege/content.write</privilege>
598 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
599 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
600 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
601 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
602 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
603 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
604 /// <exception cref="ArgumentException">
605 /// <paramref name="path"/> is a zero-length string, contains only white space.<br/>
607 /// <paramref name="path"/> contains a hidden path that starts with '.'.<br/>
609 /// <paramref name="path"/> contains a directory containing the ".scan_ignore" file.
611 /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exists.</exception>
612 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
613 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
614 /// <since_tizen> 4 </since_tizen>
615 public MediaInfo Add(string path)
619 ValidationUtil.ValidateNotNullOrEmpty(path, nameof(path));
621 if (File.Exists(path) == false)
623 throw new FileNotFoundException("destination is not valid path.", path);
626 if (File.GetAttributes(path).HasFlag(FileAttributes.Hidden))
628 throw new ArgumentException($"{nameof(path)} contains a hidden path.", nameof(path));
631 Interop.MediaInfoHandle handle = null;
635 Interop.MediaInfo.Insert(path, out handle).ThrowIfError("Failed to insert");
637 return MediaInfo.FromHandle(handle);
648 private static void ValidatePaths(IEnumerable<string> paths)
652 throw new ArgumentNullException(nameof(paths));
655 if (paths.Count() > 300)
657 throw new ArgumentException("Too many paths to add.");
660 foreach (var path in paths)
664 throw new ArgumentException($"{nameof(paths)} contains null.", nameof(paths));
667 if (File.Exists(path) == false)
669 throw new FileNotFoundException($"{nameof(paths)} contains a path that does not exist. Path={path}.", path);
676 /// Adds media files into the media database.
679 /// The paths that already exist in the database will be ignored.<br/>
680 /// At most 300 items can be added at once.<br/>
682 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.<br/>
683 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.<br/>
685 /// If http://tizen.org/feature/content.scanning.others feature is not supported and the specified file is other-type,
686 /// <see cref="NotSupportedException"/> will be thrown.
688 /// <privilege>http://tizen.org/privilege/content.write</privilege>
689 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
690 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
691 /// <param name="paths">The paths of the media files to add.</param>
692 /// <returns>A task that represents the asynchronous add operation.</returns>
693 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
694 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
695 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
696 /// <exception cref="ArgumentNullException"><paramref name="paths"/> is null.</exception>
697 /// <exception cref="ArgumentException">
698 /// <paramref name="paths"/> contains null.<br/>
700 /// <paramref name="paths"/> contains the invalid path.<br/>
702 /// The number of <paramref name="paths"/> is 300 or more items.
704 /// <exception cref="FileNotFoundException"><paramref name="paths"/> contains a path that does not exist.</exception>
705 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
706 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
707 /// <since_tizen> 4 </since_tizen>
708 public async Task AddAsync(IEnumerable<string> paths)
712 ValidatePaths(paths);
714 var pathArray = paths.ToArray();
715 var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
717 Interop.MediaInfo.InsertCompletedCallback callback = (error, _) =>
719 if (error == MediaContentError.None)
721 tcs.TrySetResult(true);
725 tcs.TrySetException(error.AsException("Failed to add"));
729 using (ObjectKeeper.Get(callback))
731 Interop.MediaInfo.BatchInsert(pathArray, pathArray.Length, callback).ThrowIfError("Failed to add");
738 /// Updates the media with the favorite value.
740 /// <privilege>http://tizen.org/privilege/content.write</privilege>
741 /// <param name="mediaId">The media ID to update.</param>
742 /// <param name="value">The value indicating whether the media is favorite.</param>
743 /// <returns>true if the matched record was found and updated, otherwise false.</returns>
744 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
745 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
746 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
747 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
748 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
749 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
750 /// <since_tizen> 4 </since_tizen>
751 [Obsolete("Deprecated since API12; Will be removed in API14.")]
752 public bool UpdateFavorite(string mediaId, bool value)
756 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
758 if (CommandHelper.Count(
759 Interop.MediaInfo.GetMediaCount, $"{MediaInfoColumns.Id}='{mediaId}'") == 0)
764 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to update");
766 if (handle.IsInvalid)
773 Interop.MediaInfo.SetFavorite(handle, value).ThrowIfError("Failed to update");
775 Interop.MediaInfo.UpdateToDB(handle).ThrowIfError("Failed to update");
785 /// Updates the path of the media to the specified destination path in the database.
787 /// <privilege>http://tizen.org/privilege/content.write</privilege>
788 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
789 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
790 /// <param name="mediaId">The media ID to move.</param>
791 /// <param name="newPath">The path that the media has been moved to.</param>
792 /// <returns>true if the matched record was found and updated, otherwise false.</returns>
794 /// Usually, it is used after the media file is moved to the another path.<br/>
796 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.<br/>
797 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
799 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
800 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
801 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
802 /// <exception cref="ArgumentNullException">
803 /// <paramref name="mediaId"/> is null.<br/>
805 /// <paramref name="newPath"/> is null.
807 /// <exception cref="ArgumentException">
808 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.<br/>
810 /// <paramref name="newPath"/> is a zero-length string, contains only white space.<br/>
812 /// <paramref name="newPath"/> contains a hidden directory that starts with '.'.<br/>
814 /// <paramref name="newPath"/> contains a directory containing the ".scan_ignore" file.
816 /// <exception cref="FileNotFoundException"><paramref name="newPath"/> does not exists.</exception>
817 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
818 /// <since_tizen> 4 </since_tizen>
819 public bool Move(string mediaId, string newPath)
823 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
825 ValidationUtil.ValidateNotNullOrEmpty(newPath, nameof(newPath));
827 if (File.Exists(newPath) == false)
829 throw new FileNotFoundException("destination is not valid path.", newPath);
832 if (File.GetAttributes(newPath).HasFlag(FileAttributes.Hidden))
834 throw new ArgumentException($"{nameof(newPath)} contains a hidden path.", nameof(newPath));
837 //TODO can be improved if MoveToDB supports result value.
838 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).
839 Ignore(MediaContentError.InvalidParameter).ThrowIfError("Failed to move");
841 if (handle.IsInvalid)
848 Interop.MediaInfo.MoveToDB(handle, newPath).ThrowIfError("Failed to move");
858 #region CreateThumbnailAsync
860 /// Creates the thumbnail image for the given media.
861 /// If the thumbnail already exists for the given media, the existing path will be returned.
863 /// <privilege>http://tizen.org/privilege/content.write</privilege>
864 /// <param name="mediaId">The media ID to create the thumbnail.</param>
865 /// <returns>A task that represents the asynchronous operation. The task result contains the thumbnail path.</returns>
866 /// <exception cref="InvalidOperationException">
867 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
869 /// An internal error occurred while executing.
871 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
872 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
873 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
874 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
875 /// <exception cref="ArgumentException">
876 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
878 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
879 /// <exception cref="UnsupportedContentException">
880 /// The thumbnail is not available for the given media.<br/>
882 /// The media is in the external USB storage.
884 /// <since_tizen> 4 </since_tizen>
885 [Obsolete("Deprecated since API10; Will be removed in API12. Please use CreateThumbnail instead.")]
886 public Task<string> CreateThumbnailAsync(string mediaId)
888 return CreateThumbnailAsync(mediaId, CancellationToken.None);
892 /// Creates the thumbnail image for the given media.
893 /// If the thumbnail already exists for the given media, the existing path will be returned.
895 /// <privilege>http://tizen.org/privilege/content.write</privilege>
896 /// <param name="mediaId">The media ID to create the thumbnail.</param>
897 /// <param name="cancellationToken">The token to cancel the operation.</param>
898 /// <returns>A task that represents the asynchronous operation. The task result contains the thumbnail path.</returns>
899 /// <exception cref="InvalidOperationException">
900 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
902 /// An internal error occurred while executing.
904 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
905 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
906 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
907 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
908 /// <exception cref="ArgumentException">
909 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
911 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
912 /// <exception cref="UnsupportedContentException">
913 /// The thumbnail is not available for the given media.<br/>
915 /// The media is in the external USB storage.
917 /// <since_tizen> 4 </since_tizen>
918 [Obsolete("Deprecated since API10; Will be removed in API12. Please use CreateThumbnail instead.")]
919 public Task<string> CreateThumbnailAsync(string mediaId, CancellationToken cancellationToken)
923 return cancellationToken.IsCancellationRequested ? Task.FromCanceled<string>(cancellationToken) :
924 CreateThumbnailAsyncCore(mediaId, cancellationToken);
927 private async Task<string> CreateThumbnailAsyncCore(string mediaId, CancellationToken cancellationToken)
929 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
931 var tcs = new TaskCompletionSource<string>();
933 using (var handle = ValidateFile(mediaId))
935 string thumbnailPath = null;
936 MediaContentError ret = MediaContentError.None;
937 Task thumbTask = null;
939 if (cancellationToken.CanBeCanceled)
941 cancellationToken.Register(() =>
943 if (tcs.Task.IsCompleted)
948 tcs.TrySetCanceled();
952 thumbTask = Task.Factory.StartNew( () =>
954 ret = Interop.MediaInfo.GenerateThumbnail(handle);
956 if (ret != MediaContentError.None)
958 tcs.TrySetException(ret.AsException("Failed to create thumbnail"));
962 thumbnailPath = InteropHelper.GetString(handle, Interop.MediaInfo.GetThumbnailPath, true);
963 tcs.TrySetResult(thumbnailPath);
965 }, cancellationToken,
966 TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning,
967 TaskScheduler.Default);
969 return await tcs.Task;
975 /// Creates the thumbnail image for the given media.
976 /// If the thumbnail already exists for the given media, the existing path will be returned.
978 /// <privilege>http://tizen.org/privilege/content.write</privilege>
979 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
980 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
981 /// <param name="mediaId">The ID of the media for which the thumbnail will be created.</param>
982 /// <returns>A created thumbnail path.</returns>
983 /// <exception cref="InvalidOperationException">
984 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
986 /// An internal error occurred while executing.
988 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
989 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
990 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
991 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
992 /// <exception cref="ArgumentException">
993 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
995 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
996 /// <exception cref="UnsupportedContentException">
997 /// The thumbnail is not available for the given media.<br/>
999 /// The media is in the external USB storage.
1001 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
1002 /// <since_tizen> 10 </since_tizen>
1003 public string CreateThumbnail(string mediaId)
1007 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
1009 using (var handle = ValidateFile(mediaId))
1011 Interop.MediaInfo.GenerateThumbnail(handle).ThrowIfError("Failed to create thumbnail");
1013 return InteropHelper.GetString(handle, Interop.MediaInfo.GetThumbnailPath, true);
1017 private Interop.MediaInfoHandle ValidateFile(string mediaId)
1019 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to create thumbnail");
1021 if (handle.IsInvalid)
1023 throw new RecordNotFoundException("Media does not exist.");
1028 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
1030 if (String.IsNullOrEmpty(path) || File.Exists(path) == false)
1032 throw new FileNotFoundException($"The media file does not exist. Path={path}.", path);
1035 foreach (var extendedInternal in StorageManager.Storages.Where(s => s.StorageType == StorageArea.ExtendedInternal))
1037 if (path.Contains(extendedInternal.RootDirectory))
1039 throw new UnsupportedContentException("The media is in external usb storage.");
1043 catch (Exception ex)
1052 #region DetectFaceAsync
1054 /// Detects faces from the given media.
1055 /// If the thumbnail already exists for the given media, the existing path will be returned.
1057 /// <privilege>http://tizen.org/privilege/content.write</privilege>
1058 /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
1059 /// <param name="mediaId">The media ID to create the thumbnail.</param>
1060 /// <returns>A task that represents the asynchronous add operation. The task result contains the number of faces detected.</returns>
1061 /// <exception cref="InvalidOperationException">
1062 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
1064 /// An internal error occurred while executing.
1066 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
1067 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
1068 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
1069 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
1070 /// <exception cref="ArgumentException">
1071 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
1073 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
1074 /// <exception cref="UnsupportedContentException">Face detection is not available for the given media.</exception>
1075 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
1076 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
1077 /// <since_tizen> 4 </since_tizen>
1078 [Obsolete("Deprecated since API11; Will be removed in API13.")]
1079 public Task<int> DetectFaceAsync(string mediaId)
1081 return DetectFaceAsync(mediaId, CancellationToken.None);
1085 /// Creates the thumbnail image for the given media.
1086 /// If the thumbnail already exists for the given media, the existing path will be returned.
1089 /// Media in the external storage is not supported, with the exception of MMC.
1090 /// Only JPEG, PNG, BMP images are supported.
1092 /// <privilege>http://tizen.org/privilege/content.write</privilege>
1093 /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
1094 /// <param name="mediaId">The media ID to create the thumbnail.</param>
1095 /// <param name="cancellationToken">The token to cancel the operation.</param>
1096 /// <returns>A task that represents the asynchronous operation. The task result contains the number of faces detected.</returns>
1097 /// <exception cref="InvalidOperationException">
1098 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
1100 /// An internal error occurred while executing.
1102 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
1103 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
1104 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
1105 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
1106 /// <exception cref="ArgumentException">
1107 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
1109 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
1110 /// <exception cref="UnsupportedContentException">
1111 /// Face detection is not available for the given media.<br/>
1113 /// The media is in the external USB storage.
1115 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
1116 /// <since_tizen> 4 </since_tizen>
1117 [Obsolete("Deprecated since API11; Will be removed in API13.")]
1118 public Task<int> DetectFaceAsync(string mediaId, CancellationToken cancellationToken)
1120 if (Features.IsSupported(Features.FaceRecognition) == false)
1122 throw new NotSupportedException($"The feature({Features.FaceRecognition}) is not supported.");
1127 return cancellationToken.IsCancellationRequested ? Task.FromCanceled<int>(cancellationToken) :
1128 DetectFaceAsyncCore(mediaId, cancellationToken);
1131 private static async Task<int> DetectFaceAsyncCore(string mediaId, CancellationToken cancellationToken)
1133 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
1135 var tcs = new TaskCompletionSource<int>();
1137 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to detect faces");
1139 if (handle.IsInvalid)
1141 throw new RecordNotFoundException("Media does not exist.");
1146 if (InteropHelper.GetValue<MediaType>(handle, Interop.MediaInfo.GetMediaType) != MediaType.Image)
1148 throw new UnsupportedContentException("Only image is supported.");
1151 // Native P/Invoke function also check below case, but it returns invalid operation error.
1152 // So we check it here to throw more proper exception.
1153 string mimeType = InteropHelper.GetString(handle, Interop.MediaInfo.GetMimeType);
1154 if (!mimeType.Equals("image/jpeg") && !mimeType.Equals("image/png") && !mimeType.Equals("image/bmp"))
1156 throw new UnsupportedContentException($"{mimeType} is not supported. Only JPEG, PNG, BMP is supported.");
1159 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
1161 if (File.Exists(path) == false)
1163 throw new FileNotFoundException($"The media file does not exist. Path={path}.", path);
1166 using (RegisterCancelFaceDetection(cancellationToken, tcs, handle))
1167 using (var cbKeeper = ObjectKeeper.Get(GetFaceDetectionCallback(tcs)))
1169 var ret = Interop.MediaInfo.StartFaceDetection(handle, cbKeeper.Target);
1170 if (ret == MediaContentError.InvalidParameter)
1172 throw new UnsupportedContentException("The media is in external usb storage.");
1175 ret.ThrowIfError("Failed to detect faces");
1177 return await tcs.Task;
1182 private static Interop.MediaInfo.FaceDetectionCompletedCallback GetFaceDetectionCallback(
1183 TaskCompletionSource<int> tcs)
1185 return (error, count, _) =>
1187 if (error != MediaContentError.None)
1189 tcs.TrySetException(error.AsException("Failed to detect faces"));
1193 tcs.TrySetResult(count);
1198 private static IDisposable RegisterCancelFaceDetection(CancellationToken cancellationToken,
1199 TaskCompletionSource<int> tcs, Interop.MediaInfoHandle handle)
1201 if (cancellationToken.CanBeCanceled == false)
1206 return cancellationToken.Register(() =>
1208 if (tcs.Task.IsCompleted)
1213 Interop.MediaInfo.CancelFaceDetection(handle).ThrowIfError("Failed to cancel");
1214 tcs.TrySetCanceled();