2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 using System.Collections.Generic;
21 using System.Threading;
22 using System.Threading.Tasks;
25 namespace Tizen.Content.MediaContent
28 /// Provides commands to manage the media information and query related items in the database.
30 /// <since_tizen> 4 </since_tizen>
31 public class MediaInfoCommand : MediaCommand
34 /// Initializes a new instance of the <see cref="FolderCommand"/> class with the specified <see cref="MediaDatabase"/>.
36 /// <param name="database">The <see cref="MediaDatabase"/> that the commands run on.</param>
37 /// <exception cref="ArgumentNullException"><paramref name="database"/> is null.</exception>
38 /// <exception cref="ObjectDisposedException"><paramref name="database"/> has already been disposed.</exception>
39 /// <since_tizen> 4 </since_tizen>
40 public MediaInfoCommand(MediaDatabase database) : base(database)
45 /// Retrieves the number of the bookmarks added to the media.
47 /// <param name="mediaId">The media ID to count the bookmarks added to the media.</param>
48 /// <returns>The number of the bookmarks.</returns>
49 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
50 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
51 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
52 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
53 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
54 /// <since_tizen> 4 </since_tizen>
55 public int CountBookmark(string mediaId)
57 return CountBookmark(mediaId, null);
61 /// Retrieves the number of the bookmarks added to the media with the <see cref="CountArguments"/>.
63 /// <param name="mediaId">The media ID to count the bookmarks added to the media.</param>
64 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
65 /// <returns>The number of the bookmarks.</returns>
66 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
67 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
68 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
69 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
70 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
71 /// <since_tizen> 4 </since_tizen>
72 public int CountBookmark(string mediaId, CountArguments arguments)
76 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
78 return CommandHelper.Count(Interop.MediaInfo.GetBookmarkCount, mediaId, arguments);
82 /// Retrieves the bookmarks added to the media.
84 /// <param name="mediaId">The media ID to select the bookmarks added to the media.</param>
85 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
86 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
87 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
88 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
89 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
90 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
91 /// <since_tizen> 4 </since_tizen>
92 public MediaDataReader<Bookmark> SelectBookmark(string mediaId)
94 return SelectBookmark(mediaId, null);
98 /// Retrieves the bookmarks added to the media with the <see cref="SelectArguments"/>.
100 /// <param name="mediaId">The media ID to select the bookmarks added to the media.</param>
101 /// <param name="filter">The criteria to use to filter. This value can be null.</param>
102 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
103 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
104 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
105 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
106 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
107 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
108 /// <since_tizen> 4 </since_tizen>
109 public MediaDataReader<Bookmark> SelectBookmark(string mediaId, SelectArguments filter)
113 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
115 return CommandHelper.SelectMembers(mediaId, filter, Interop.MediaInfo.ForeachBookmarks,
116 Bookmark.FromHandle);
120 /// Retrieves the number of the face information added to or detected from the media.
122 /// <param name="mediaId">The media ID to count face information added to the media.</param>
123 /// <returns>The number of the face information.</returns>
124 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
125 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
126 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
127 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
128 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
129 /// <since_tizen> 4 </since_tizen>
130 public int CountFaceInfo(string mediaId)
132 return CountFaceInfo(mediaId, null);
136 /// Retrieves the number of the face information added to or detected from the media with filter.
138 /// <param name="mediaId">The media ID to count the face information added to the media.</param>
139 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
140 /// <returns>The number of the face information.</returns>
141 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
142 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
143 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
144 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
145 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
146 /// <since_tizen> 4 </since_tizen>
147 public int CountFaceInfo(string mediaId, CountArguments arguments)
151 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
153 return CommandHelper.Count(Interop.MediaInfo.GetFaceCount, mediaId, arguments);
157 /// Retrieves the face information added to or detected from the media.
159 /// <param name="mediaId">The media ID to select face information added to the media.</param>
160 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
161 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
162 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
163 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
164 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
165 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
166 /// <since_tizen> 4 </since_tizen>
167 public MediaDataReader<FaceInfo> SelectFaceInfo(string mediaId)
169 return SelectFaceInfo(mediaId, null);
173 /// Retrieves the face information added to or detected from the media with the <see cref="SelectArguments"/>.
175 /// <param name="mediaId">The media ID to select the face information added to the media.</param>
176 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
177 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
178 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
179 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
180 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
181 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
182 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
183 /// <since_tizen> 4 </since_tizen>
184 public MediaDataReader<FaceInfo> SelectFaceInfo(string mediaId, SelectArguments arguments)
188 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
190 return CommandHelper.SelectMembers(mediaId, arguments, Interop.MediaInfo.ForeachFaces,
191 FaceInfo.FromHandle);
195 /// Retrieves the number of tags that the media has.
197 /// <returns>The number of tags.</returns>
198 /// <param name="mediaId">The media ID to count tags added to the media.</param>
199 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
200 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
201 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
202 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
203 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
204 /// <since_tizen> 4 </since_tizen>
205 public int CountTag(string mediaId)
207 return CountTag(mediaId, null);
211 /// Retrieves the number of tags that the media has with the <see cref="CountArguments"/>.
213 /// <param name="mediaId">The media ID to count tags added to the media.</param>
214 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
215 /// <returns>The number of tags.</returns>
216 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
217 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
218 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
219 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
220 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
221 /// <since_tizen> 4 </since_tizen>
222 public int CountTag(string mediaId, CountArguments arguments)
226 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
228 return CommandHelper.Count(Interop.MediaInfo.GetTagCount, mediaId, arguments);
232 /// Retrieves the tags that the media has.
234 /// <param name="mediaId">The media ID to select tags added to the media.</param>
235 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
236 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
237 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
238 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
239 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
240 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
241 /// <since_tizen> 4 </since_tizen>
242 public MediaDataReader<Tag> SelectTag(string mediaId)
244 return SelectTag(mediaId, null);
248 /// Retrieves the tags that the media has with the <see cref="SelectArguments"/>.
250 /// <param name="mediaId">The media ID to select tags added to the media.</param>
251 /// <param name="filter">The criteria to use to filter. This value can be null.</param>
252 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
253 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
254 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
255 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
256 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
257 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
258 /// <since_tizen> 4 </since_tizen>
259 public MediaDataReader<Tag> SelectTag(string mediaId, SelectArguments filter)
263 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
265 return CommandHelper.SelectMembers(mediaId, filter, Interop.MediaInfo.ForeachTags,
270 /// Retrieves the number of the media information.
272 /// <returns>The number of the media information.</returns>
273 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
274 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
275 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
276 /// <since_tizen> 4 </since_tizen>
277 public int CountMedia()
279 return CountMedia(null);
283 /// Retrieves the number of the media information with the <see cref="SelectArguments"/>.
285 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
286 /// <returns>The number of media information.</returns>
287 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
288 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
289 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
290 /// <since_tizen> 4 </since_tizen>
291 public int CountMedia(CountArguments arguments)
295 return CommandHelper.Count(Interop.MediaInfo.GetMediaCount, arguments);
299 /// Retrieves the media.
301 /// <param name="mediaId">The media ID to retrieve.</param>
302 /// <returns>The <see cref="MediaInfo"/> instance if the matched record was found in the database, otherwise null.</returns>
303 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
304 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
305 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
306 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
307 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
308 /// <since_tizen> 4 </since_tizen>
309 public MediaInfo SelectMedia(string mediaId)
313 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
315 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).Ignore(MediaContentError.InvalidParameter).
316 ThrowIfError("Failed to query");
320 return MediaInfo.FromHandle(handle);
329 /// Retrieves the number of values grouped by the specified column with the <see cref="SelectArguments"/>.
331 /// <param name="columnKey">The column key.</param>
332 /// <returns>The number of groups.</returns>
333 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
334 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
335 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
336 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
337 /// <since_tizen> 4 </since_tizen>
338 public int CountGroupBy(MediaInfoColumnKey columnKey)
340 return CountGroupBy(columnKey, null);
344 /// Retrieves the number of values grouped by the specified column with the <see cref="SelectArguments"/>.
346 /// <param name="columnKey">The column key.</param>
347 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
348 /// <returns>The number of groups.</returns>
349 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
350 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
351 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
352 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
353 /// <since_tizen> 4 </since_tizen>
354 public int CountGroupBy(MediaInfoColumnKey columnKey, CountArguments arguments)
358 ValidationUtil.ValidateEnum(typeof(MediaInfoColumnKey), columnKey, nameof(columnKey));
360 using (var filter = QueryArguments.ToNativeHandle(arguments))
362 Interop.Group.GetGroupCount(filter, columnKey, out var count).ThrowIfError("Failed to query count");
368 /// Retrieves the group values of the specified column.
370 /// <param name="columnKey">The column key.</param>
371 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
372 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
373 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
374 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
375 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
376 /// <since_tizen> 4 </since_tizen>
377 public MediaDataReader<string> SelectGroupBy(MediaInfoColumnKey columnKey)
379 return SelectGroupBy(columnKey, null);
383 /// Retrieves the group values of the specified column with the <see cref="SelectArguments"/>.
385 /// <param name="columnKey">The column key.</param>
386 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
387 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
388 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
389 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
390 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
391 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
392 /// <since_tizen> 4 </since_tizen>
393 public MediaDataReader<string> SelectGroupBy(MediaInfoColumnKey columnKey, SelectArguments arguments)
397 ValidationUtil.ValidateEnum(typeof(MediaInfoColumnKey), columnKey, nameof(columnKey));
399 List<string> list = new List<string>();
401 using (var filter = QueryArguments.ToNativeHandle(arguments))
403 Interop.Group.ForeachGroup(filter, columnKey, (name, _) =>
408 }).ThrowIfError("Failed to query");
410 return new MediaDataReader<string>(list);
415 /// Retrieves all the media.
417 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
418 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
419 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
420 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
421 /// <since_tizen> 4 </since_tizen>
422 public MediaDataReader<MediaInfo> SelectMedia()
424 return SelectMedia(arguments: null);
428 /// Retrieves the media with the <see cref="SelectArguments"/>.
430 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
431 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
432 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
433 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
434 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
435 /// <since_tizen> 4 </since_tizen>
436 public MediaDataReader<MediaInfo> SelectMedia(SelectArguments arguments)
440 return new MediaDataReader<MediaInfo>(QueryMedia(arguments));
443 private static List<MediaInfo> QueryMedia(SelectArguments arguments)
445 using (var filter = QueryArguments.ToNativeHandle(arguments))
447 List<MediaInfo> list = new List<MediaInfo>();
449 Exception caught = null;
451 Interop.MediaInfo.ForeachMedia(filter, (handle, _) =>
455 list.Add(MediaInfo.FromHandle(handle));
475 /// Deletes the media from the database.
477 /// <privilege>http://tizen.org/privilege/content.write</privilege>
478 /// <param name="mediaId">The media ID to delete.</param>
479 /// <returns>true if the matched record was found and deleted, otherwise false.</returns>
481 /// The <see cref="MediaDatabase.ScanFile(string)"/> or the <see cref="MediaDatabase.ScanFolderAsync(string)"/> can be used instead.<br/>
482 /// Since API level 6, if the file related with the <paramref name="mediaId"/> in DB still exists in file system before calling this method,
483 /// <see cref="InvalidOperationException"/> will be thrown to keep consistency in DB.
485 /// <exception cref="InvalidOperationException">
486 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
488 /// The file related with the <paramref name="mediaId"/> in DB still exists in file system. (Since API level 6)
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="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
493 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
494 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
495 /// <since_tizen> 4 </since_tizen>
496 public bool Delete(string mediaId)
500 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
502 if (CommandHelper.Count(
503 Interop.MediaInfo.GetMediaCount, $"{MediaInfoColumns.Id}='{mediaId}'") == 0)
508 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).
509 ThrowIfError("Failed to delete MediaInfo.");
511 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
513 // If we don't check file existence before calling `ScanFile` method,
514 // The inconsistency between DB and file system could be occurred.
515 if (File.Exists(path))
517 throw new InvalidOperationException("File still exists in file system. Remove it first.");
520 // Native 'delete' function was deprecated, so we need to use 'scan file' function instead of it.
521 Database.ScanFile(path);
527 /// Adds the media to the database.
529 /// <param name="path">The file path to add.</param>
530 /// <returns>The <see cref="MediaInfo"/> instance that contains the record information in the database.</returns>
532 /// If the media already exists in the database, it returns the existing information.<br/>
534 /// The <see cref="MediaDatabase.ScanFile(string)"/> or the <see cref="MediaDatabase.ScanFolderAsync(string)"/> can be used instead.<br/>
536 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.<br/>
537 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.<br/>
539 /// If http://tizen.org/feature/content.scanning.others feature is not supported and the specified file is other-type,
540 /// <see cref="NotSupportedException"/> will be thrown.
542 /// <privilege>http://tizen.org/privilege/content.write</privilege>
543 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
544 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
545 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
546 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
547 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
548 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
549 /// <exception cref="ArgumentException">
550 /// <paramref name="path"/> is a zero-length string, contains only white space.<br/>
552 /// <paramref name="path"/> contains a hidden path that starts with '.'.<br/>
554 /// <paramref name="path"/> contains a directory containing the ".scan_ignore" file.
556 /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exists.</exception>
557 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
558 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
559 /// <since_tizen> 4 </since_tizen>
560 public MediaInfo Add(string path)
564 ValidationUtil.ValidateNotNullOrEmpty(path, nameof(path));
566 if (File.Exists(path) == false)
568 throw new FileNotFoundException("destination is not valid path.", path);
571 if (File.GetAttributes(path).HasFlag(FileAttributes.Hidden))
573 throw new ArgumentException($"{nameof(path)} contains a hidden path.", nameof(path));
576 Interop.MediaInfoHandle handle = null;
580 Interop.MediaInfo.Insert(path, out handle).ThrowIfError("Failed to insert");
582 return MediaInfo.FromHandle(handle);
593 private static void ValidatePaths(IEnumerable<string> paths)
597 throw new ArgumentNullException(nameof(paths));
600 if (paths.Count() > 300)
602 throw new ArgumentException("Too many paths to add.");
605 foreach (var path in paths)
609 throw new ArgumentException($"{nameof(paths)} contains null.", nameof(paths));
612 if (File.Exists(path) == false)
614 throw new FileNotFoundException($"{nameof(paths)} contains a path that does not exist. Path={path}.", path);
621 /// Adds media files into the media database.
624 /// The paths that already exist in the database will be ignored.<br/>
625 /// At most 300 items can be added at once.<br/>
627 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.<br/>
628 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.<br/>
630 /// If http://tizen.org/feature/content.scanning.others feature is not supported and the specified file is other-type,
631 /// <see cref="NotSupportedException"/> will be thrown.
633 /// <privilege>http://tizen.org/privilege/content.write</privilege>
634 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
635 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
636 /// <param name="paths">The paths of the media files to add.</param>
637 /// <returns>A task that represents the asynchronous add operation.</returns>
638 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
639 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
640 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
641 /// <exception cref="ArgumentNullException"><paramref name="paths"/> is null.</exception>
642 /// <exception cref="ArgumentException">
643 /// <paramref name="paths"/> contains null.<br/>
645 /// <paramref name="paths"/> contains the invalid path.<br/>
647 /// The number of <paramref name="paths"/> is 300 or more items.
649 /// <exception cref="FileNotFoundException"><paramref name="paths"/> contains a path that does not exist.</exception>
650 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
651 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
652 /// <since_tizen> 4 </since_tizen>
653 public async Task AddAsync(IEnumerable<string> paths)
657 ValidatePaths(paths);
659 var pathArray = paths.ToArray();
660 var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
662 Interop.MediaInfo.InsertCompletedCallback callback = (error, _) =>
664 if (error == MediaContentError.None)
666 tcs.TrySetResult(true);
670 tcs.TrySetException(error.AsException("Failed to add"));
674 using (ObjectKeeper.Get(callback))
676 Interop.MediaInfo.BatchInsert(pathArray, pathArray.Length, callback).ThrowIfError("Failed to add");
683 /// Updates the media with the favorite value.
685 /// <privilege>http://tizen.org/privilege/content.write</privilege>
686 /// <param name="mediaId">The media ID to update.</param>
687 /// <param name="value">The value indicating whether the media is favorite.</param>
688 /// <returns>true if the matched record was found and updated, otherwise false.</returns>
689 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
690 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
691 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
692 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
693 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
694 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
695 /// <since_tizen> 4 </since_tizen>
696 public bool UpdateFavorite(string mediaId, bool value)
700 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
702 if (CommandHelper.Count(
703 Interop.MediaInfo.GetMediaCount, $"{MediaInfoColumns.Id}='{mediaId}'") == 0)
708 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to update");
710 if (handle.IsInvalid)
717 Interop.MediaInfo.SetFavorite(handle, value).ThrowIfError("Failed to update");
719 Interop.MediaInfo.UpdateToDB(handle).ThrowIfError("Failed to update");
729 /// Updates the path of the media to the specified destination path in the database.
731 /// <privilege>http://tizen.org/privilege/content.write</privilege>
732 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
733 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
734 /// <param name="mediaId">The media ID to move.</param>
735 /// <param name="newPath">The path that the media has been moved to.</param>
736 /// <returns>true if the matched record was found and updated, otherwise false.</returns>
738 /// Usually, it is used after the media file is moved to the another path.<br/>
740 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.<br/>
741 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
743 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
744 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
745 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
746 /// <exception cref="ArgumentNullException">
747 /// <paramref name="mediaId"/> is null.<br/>
749 /// <paramref name="newPath"/> is null.
751 /// <exception cref="ArgumentException">
752 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.<br/>
754 /// <paramref name="newPath"/> is a zero-length string, contains only white space.<br/>
756 /// <paramref name="newPath"/> contains a hidden directory that starts with '.'.<br/>
758 /// <paramref name="newPath"/> contains a directory containing the ".scan_ignore" file.
760 /// <exception cref="FileNotFoundException"><paramref name="newPath"/> does not exists.</exception>
761 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
762 /// <since_tizen> 4 </since_tizen>
763 public bool Move(string mediaId, string newPath)
767 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
769 ValidationUtil.ValidateNotNullOrEmpty(newPath, nameof(newPath));
771 if (File.Exists(newPath) == false)
773 throw new FileNotFoundException("destination is not valid path.", newPath);
776 if (File.GetAttributes(newPath).HasFlag(FileAttributes.Hidden))
778 throw new ArgumentException($"{nameof(newPath)} contains a hidden path.", nameof(newPath));
781 //TODO can be improved if MoveToDB supports result value.
782 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).
783 Ignore(MediaContentError.InvalidParameter).ThrowIfError("Failed to move");
785 if (handle.IsInvalid)
792 Interop.MediaInfo.MoveToDB(handle, newPath).ThrowIfError("Failed to move");
802 #region CreateThumbnailAsync
804 /// Creates the thumbnail image for the given media.
805 /// If the thumbnail already exists for the given media, the existing path will be returned.
807 /// <privilege>http://tizen.org/privilege/content.write</privilege>
808 /// <param name="mediaId">The media ID to create the thumbnail.</param>
809 /// <returns>A task that represents the asynchronous operation. The task result contains the thumbnail path.</returns>
810 /// <exception cref="InvalidOperationException">
811 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
813 /// An internal error occurred while executing.
815 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
816 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
817 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
818 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
819 /// <exception cref="ArgumentException">
820 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
822 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
823 /// <exception cref="UnsupportedContentException">
824 /// The thumbnail is not available for the given media.<br/>
826 /// The media is in the external USB storage (<see cref="MediaInfo.StorageType"/> is <see cref="StorageType.ExternalUsb"/>).
828 /// <since_tizen> 4 </since_tizen>
829 public Task<string> CreateThumbnailAsync(string mediaId)
831 return CreateThumbnailAsync(mediaId, CancellationToken.None);
835 /// Creates the thumbnail image for the given media.
836 /// If the thumbnail already exists for the given media, the existing path will be returned.
838 /// <privilege>http://tizen.org/privilege/content.write</privilege>
839 /// <param name="mediaId">The media ID to create the thumbnail.</param>
840 /// <param name="cancellationToken">The token to cancel the operation.</param>
841 /// <returns>A task that represents the asynchronous operation. The task result contains the thumbnail path.</returns>
842 /// <exception cref="InvalidOperationException">
843 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
845 /// An internal error occurred while executing.
847 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
848 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
849 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
850 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
851 /// <exception cref="ArgumentException">
852 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
854 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
855 /// <exception cref="UnsupportedContentException">
856 /// The thumbnail is not available for the given media.<br/>
858 /// The media is in the external USB storage (<see cref="MediaInfo.StorageType"/> is <see cref="StorageType.ExternalUsb"/>).
860 /// <since_tizen> 4 </since_tizen>
861 public Task<string> CreateThumbnailAsync(string mediaId, CancellationToken cancellationToken)
865 return cancellationToken.IsCancellationRequested ? Task.FromCanceled<string>(cancellationToken) :
866 CreateThumbnailAsyncCore(mediaId, cancellationToken);
869 private async Task<string> CreateThumbnailAsyncCore(string mediaId, CancellationToken cancellationToken)
871 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
873 var tcs = new TaskCompletionSource<string>();
875 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to create thumbnail");
877 if (handle.IsInvalid)
879 throw new RecordNotFoundException("Media does not exist.");
884 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
886 foreach (var extendedInternal in StorageManager.Storages.Where(s => s.StorageType == StorageArea.ExtendedInternal))
888 if (path.Contains(extendedInternal.RootDirectory))
890 throw new UnsupportedContentException("The media is in external usb storage.");
894 if (File.Exists(path) == false)
896 throw new FileNotFoundException($"The media file does not exist. Path={path}.", path);
899 using (RegisterCancelThumbnail(cancellationToken, tcs, handle))
900 using (var cbKeeper = ObjectKeeper.Get(GetCreateThumbnailCallback(tcs)))
902 Interop.MediaInfo.CreateThumbnail(handle, cbKeeper.Target).ThrowIfError("Failed to create thumbnail");
904 return await tcs.Task;
909 private static Interop.MediaInfo.ThumbnailCompletedCallback GetCreateThumbnailCallback(
910 TaskCompletionSource<string> tcs)
912 return (error, path, _) =>
914 if (error != MediaContentError.None)
916 tcs.TrySetException(error.AsException("Failed to create thumbnail"));
920 tcs.TrySetResult(path);
925 private static IDisposable RegisterCancelThumbnail(CancellationToken cancellationToken,
926 TaskCompletionSource<string> tcs, Interop.MediaInfoHandle handle)
928 if (cancellationToken.CanBeCanceled == false)
933 return cancellationToken.Register(() =>
935 if (tcs.Task.IsCompleted)
940 Interop.MediaInfo.CancelThumbnail(handle).ThrowIfError("Failed to cancel");
941 tcs.TrySetCanceled();
946 #region DetectFaceAsync
948 /// Detects faces from the given media.
949 /// If the thumbnail already exists for the given media, the existing path will be returned.
951 /// <privilege>http://tizen.org/privilege/content.write</privilege>
952 /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
953 /// <param name="mediaId">The media ID to create the thumbnail.</param>
954 /// <returns>A task that represents the asynchronous add operation. The task result contains the number of faces detected.</returns>
955 /// <exception cref="InvalidOperationException">
956 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
958 /// An internal error occurred while executing.
960 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
961 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
962 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
963 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
964 /// <exception cref="ArgumentException">
965 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
967 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
968 /// <exception cref="UnsupportedContentException">Face detection is not available for the given media.</exception>
969 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
970 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
971 /// <since_tizen> 4 </since_tizen>
972 public Task<int> DetectFaceAsync(string mediaId)
974 return DetectFaceAsync(mediaId, CancellationToken.None);
978 /// Creates the thumbnail image for the given media.
979 /// If the thumbnail already exists for the given media, the existing path will be returned.
982 /// Media in the external storage is not supported, with the exception of MMC.
983 /// Only JPEG, PNG, BMP images are supported.
985 /// <privilege>http://tizen.org/privilege/content.write</privilege>
986 /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
987 /// <param name="mediaId">The media ID to create the thumbnail.</param>
988 /// <param name="cancellationToken">The token to cancel the operation.</param>
989 /// <returns>A task that represents the asynchronous operation. The task result contains the number of faces detected.</returns>
990 /// <exception cref="InvalidOperationException">
991 /// The <see cref="MediaDatabase"/> is disconnected.<br/>
993 /// An internal error occurred while executing.
995 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed.</exception>
996 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
997 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
998 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
999 /// <exception cref="ArgumentException">
1000 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
1002 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
1003 /// <exception cref="UnsupportedContentException">
1004 /// Face detection is not available for the given media.<br/>
1006 /// The media is in the external USB storage (<see cref="MediaInfo.StorageType"/> is <see cref="StorageType.ExternalUsb"/>).
1008 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
1009 /// <since_tizen> 4 </since_tizen>
1010 public Task<int> DetectFaceAsync(string mediaId, CancellationToken cancellationToken)
1012 if (Features.IsSupported(Features.FaceRecognition) == false)
1014 throw new NotSupportedException($"The feature({Features.FaceRecognition}) is not supported.");
1019 return cancellationToken.IsCancellationRequested ? Task.FromCanceled<int>(cancellationToken) :
1020 DetectFaceAsyncCore(mediaId, cancellationToken);
1023 private static async Task<int> DetectFaceAsyncCore(string mediaId, CancellationToken cancellationToken)
1025 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
1027 var tcs = new TaskCompletionSource<int>();
1029 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to detect faces");
1031 if (handle.IsInvalid)
1033 throw new RecordNotFoundException("Media does not exist.");
1038 if (InteropHelper.GetValue<StorageType>(handle, Interop.MediaInfo.GetStorageType) == StorageType.ExternalUsb)
1040 throw new UnsupportedContentException("The media is in external usb storage.");
1043 if (InteropHelper.GetValue<MediaType>(handle, Interop.MediaInfo.GetMediaType) != MediaType.Image)
1045 throw new UnsupportedContentException("Only image is supported.");
1048 // Native P/Invoke function also check below case, but it returns invalid operation error.
1049 // So we check it here to throw more proper exception.
1050 string mimeType = InteropHelper.GetString(handle, Interop.MediaInfo.GetMimeType);
1051 if (!mimeType.Equals("image/jpeg") && !mimeType.Equals("image/png") && !mimeType.Equals("image/bmp"))
1053 throw new UnsupportedContentException($"{mimeType} is not supported. Only JPEG, PNG, BMP is supported.");
1056 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
1058 if (File.Exists(path) == false)
1060 throw new FileNotFoundException($"The media file does not exist. Path={path}.", path);
1063 using (RegisterCancelFaceDetection(cancellationToken, tcs, handle))
1064 using (var cbKeeper = ObjectKeeper.Get(GetFaceDetectionCallback(tcs)))
1066 Interop.MediaInfo.StartFaceDetection(handle, cbKeeper.Target).ThrowIfError("Failed to detect faces");
1068 return await tcs.Task;
1073 private static Interop.MediaInfo.FaceDetectionCompletedCallback GetFaceDetectionCallback(
1074 TaskCompletionSource<int> tcs)
1076 return (error, count, _) =>
1078 if (error != MediaContentError.None)
1080 tcs.TrySetException(error.AsException("Failed to detect faces"));
1084 tcs.TrySetResult(count);
1089 private static IDisposable RegisterCancelFaceDetection(CancellationToken cancellationToken,
1090 TaskCompletionSource<int> tcs, Interop.MediaInfoHandle handle)
1092 if (cancellationToken.CanBeCanceled == false)
1097 return cancellationToken.Register(() =>
1099 if (tcs.Task.IsCompleted)
1104 Interop.MediaInfo.CancelFaceDetection(handle).ThrowIfError("Failed to cancel");
1105 tcs.TrySetCanceled();