2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 using System.Collections.Generic;
21 using System.Threading;
22 using System.Threading.Tasks;
24 namespace Tizen.Content.MediaContent
27 /// Provides commands to manage the media information and query related items in the database.
29 public class MediaInfoCommand : MediaCommand
32 /// Initializes a new instance of the <see cref="FolderCommand"/> class with the specified <see cref="MediaDatabase"/>.
34 /// <param name="database">The <see cref="MediaDatabase"/> that the commands run on.</param>
35 /// <exception cref="ArgumentNullException"><paramref name="database"/> is null.</exception>
36 /// <exception cref="ObjectDisposedException"><paramref name="database"/> has already been disposed of.</exception>
37 public MediaInfoCommand(MediaDatabase database) : base(database)
42 /// Retrieves the number of the bookmarks added to the media.
44 /// <param name="mediaId">The media ID to count the bookmarks added to the media.</param>
45 /// <returns>The number of the bookmarks.</returns>
46 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
47 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
48 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
49 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
50 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
51 public int CountBookmark(string mediaId)
53 return CountBookmark(mediaId, null);
57 /// Retrieves the number of the bookmarks added to the media with the <see cref="CountArguments"/>.
59 /// <param name="mediaId">The media ID to count the bookmarks added to the media.</param>
60 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
61 /// <returns>The number of the bookmarks.</returns>
62 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
63 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
64 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
65 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
66 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
67 public int CountBookmark(string mediaId, CountArguments arguments)
71 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
73 return CommandHelper.Count(Interop.MediaInfo.GetBookmarkCount, mediaId, arguments);
77 /// Retrieves the bookmarks added to the media.
79 /// <param name="mediaId">The media ID to select the bookmarks added to the media.</param>
80 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
81 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
82 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
83 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
84 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
85 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
86 public MediaDataReader<Bookmark> SelectBookmark(string mediaId)
88 return SelectBookmark(mediaId, null);
92 /// Retrieves the bookmarks added to the media with the <see cref="SelectArguments"/>.
94 /// <param name="mediaId">The media ID to select the bookmarks added to the media.</param>
95 /// <param name="filter">The criteria to use to filter. This value can be null.</param>
96 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
97 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
98 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
99 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
100 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
101 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
102 public MediaDataReader<Bookmark> SelectBookmark(string mediaId, SelectArguments filter)
106 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
108 return CommandHelper.SelectMembers(mediaId, filter, Interop.MediaInfo.ForeachBookmarks,
109 Bookmark.FromHandle);
113 /// Retrieves the number of the face information added to or detected from the media.
115 /// <param name="mediaId">The media ID to count face information added to the media.</param>
116 /// <returns>The number of the face information.</returns>
117 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
118 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
119 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
120 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
121 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
122 public int CountFaceInfo(string mediaId)
124 return CountFaceInfo(mediaId, null);
128 /// Retrieves the number of the face information added to or detected from the media with filter.
130 /// <param name="mediaId">The media ID to count the face information added to the media.</param>
131 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
132 /// <returns>The number of the face information.</returns>
133 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
134 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
135 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
136 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
137 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
138 public int CountFaceInfo(string mediaId, CountArguments arguments)
142 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
144 return CommandHelper.Count(Interop.MediaInfo.GetFaceCount, mediaId, arguments);
148 /// Retrieves the face information added to or detected from the media.
150 /// <param name="mediaId">The media ID to select face information added to the media.</param>
151 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
152 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
153 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
154 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
155 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
156 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
157 public MediaDataReader<FaceInfo> SelectFaceInfo(string mediaId)
159 return SelectFaceInfo(mediaId, null);
163 /// Retrieves the face information added to or detected from the media with the <see cref="SelectArguments"/>.
165 /// <param name="mediaId">The media ID to select the face information added to the media.</param>
166 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
167 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
168 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
169 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
170 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
171 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
172 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
173 public MediaDataReader<FaceInfo> SelectFaceInfo(string mediaId, SelectArguments arguments)
177 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
179 return CommandHelper.SelectMembers(mediaId, arguments, Interop.MediaInfo.ForeachFaces,
180 FaceInfo.FromHandle);
184 /// Retrieves the number of tags that the media has.
186 /// <returns>The number of tags.</returns>
187 /// <param name="mediaId">The media ID to count tags added to the media.</param>
188 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
189 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
190 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
191 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
192 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
193 public int CountTag(string mediaId)
195 return CountTag(mediaId, null);
199 /// Retrieves the number of tags that the media has with the <see cref="CountArguments"/>.
201 /// <param name="mediaId">The media ID to count tags added to the media.</param>
202 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
203 /// <returns>The number of tags.</returns>
204 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
205 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
206 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
207 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
208 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
209 public int CountTag(string mediaId, CountArguments arguments)
213 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
215 return CommandHelper.Count(Interop.MediaInfo.GetTagCount, mediaId, arguments);
219 /// Retrieves the tags that the media has.
221 /// <param name="mediaId">The media ID to select tags added to the media.</param>
222 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
223 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
224 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
225 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
226 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
227 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
228 public MediaDataReader<Tag> SelectTag(string mediaId)
230 return SelectTag(mediaId, null);
234 /// Retrieves the tags that the media has with the <see cref="SelectArguments"/>.
236 /// <param name="mediaId">The media ID to select tags added to the media.</param>
237 /// <param name="filter">The criteria to use to filter. This value can be null.</param>
238 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
239 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
240 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
241 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
242 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
243 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
244 public MediaDataReader<Tag> SelectTag(string mediaId, SelectArguments filter)
248 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
250 return CommandHelper.SelectMembers(mediaId, filter, Interop.MediaInfo.ForeachTags,
255 /// Retrieves the number of the media information.
257 /// <returns>The number of the media information.</returns>
258 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
259 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
260 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
261 public int CountMedia()
263 return CountMedia(null);
267 /// Retrieves the number of the media information with the <see cref="SelectArguments"/>.
269 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
270 /// <returns>The number of media information.</returns>
271 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
272 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
273 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
274 public int CountMedia(CountArguments arguments)
278 return CommandHelper.Count(Interop.MediaInfo.GetMediaCount, arguments);
282 /// Retrieves the media.
284 /// <param name="mediaId">The media ID to retrieve.</param>
285 /// <returns>The <see cref="MediaInfo"/> instance if the matched record was found in the database, otherwise null.</returns>
286 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
287 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
288 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
289 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
290 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
291 public MediaInfo SelectMedia(string mediaId)
295 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
297 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).Ignore(MediaContentError.InvalidParameter).
298 ThrowIfError("Failed to query");
302 return MediaInfo.FromHandle(handle);
311 /// Retrieves the number of values grouped by the specified column with the <see cref="SelectArguments"/>.
313 /// <param name="columnKey">The column key.</param>
314 /// <returns>The number of groups.</returns>
315 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
316 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
317 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
318 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
319 public int CountGroupBy(MediaInfoColumnKey columnKey)
321 return CountGroupBy(columnKey, null);
325 /// Retrieves the number of values grouped by the specified column with the <see cref="SelectArguments"/>.
327 /// <param name="columnKey">The column key.</param>
328 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
329 /// <returns>The number of groups.</returns>
330 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
331 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
332 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
333 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
334 public int CountGroupBy(MediaInfoColumnKey columnKey, CountArguments arguments)
338 ValidationUtil.ValidateEnum(typeof(MediaInfoColumnKey), columnKey, nameof(columnKey));
340 using (var filter = QueryArguments.ToNativeHandle(arguments))
342 Interop.Group.GetGroupCount(filter, columnKey, out var count).ThrowIfError("Failed to query count");
348 /// Retrieves the group values of the specified column.
350 /// <param name="columnKey">The column key.</param>
351 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
352 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
353 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
354 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
355 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
356 public MediaDataReader<string> SelectGroupBy(MediaInfoColumnKey columnKey)
358 return SelectGroupBy(columnKey, null);
362 /// Retrieves the group values of the specified column with the <see cref="SelectArguments"/>.
364 /// <param name="columnKey">The column key.</param>
365 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
366 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
367 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
368 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
369 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
370 /// <exception cref="ArgumentException"><paramref name="columnKey"/> is invalid.</exception>
371 public MediaDataReader<string> SelectGroupBy(MediaInfoColumnKey columnKey, SelectArguments arguments)
375 ValidationUtil.ValidateEnum(typeof(MediaInfoColumnKey), columnKey, nameof(columnKey));
377 List<string> list = new List<string>();
379 using (var filter = QueryArguments.ToNativeHandle(arguments))
381 Interop.Group.ForeachGroup(filter, columnKey, (name, _) =>
386 }).ThrowIfError("Failed to query");
388 return new MediaDataReader<string>(list);
393 /// Retrieves all the media.
395 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
396 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
397 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
398 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
399 public MediaDataReader<MediaInfo> SelectMedia()
401 return SelectMedia(arguments: null);
405 /// Retrieves the media with the <see cref="SelectArguments"/>.
407 /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
408 /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
409 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
410 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
411 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
412 public MediaDataReader<MediaInfo> SelectMedia(SelectArguments arguments)
416 return new MediaDataReader<MediaInfo>(QueryMedia(arguments));
419 private static List<MediaInfo> QueryMedia(SelectArguments arguments)
421 using (var filter = QueryArguments.ToNativeHandle(arguments))
423 List<MediaInfo> list = new List<MediaInfo>();
425 Exception caught = null;
427 Interop.MediaInfo.ForeachMedia(filter, (handle, _) =>
431 list.Add(MediaInfo.FromHandle(handle));
451 /// Deletes the media from the database.
453 /// <privilege>http://tizen.org/privilege/content.write</privilege>
454 /// <param name="mediaId">The media ID to delete.</param>
455 /// <returns>true if the matched record was found and deleted, otherwise false.</returns>
456 /// <remarks>The <see cref="MediaDatabase.ScanFile(string)"/> or the <see cref="MediaDatabase.ScanFolderAsync(string)"/> can be used instead.</remarks>
457 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
458 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
459 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
460 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
461 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
462 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
463 public bool Delete(string mediaId)
467 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
469 if (CommandHelper.Count(
470 Interop.MediaInfo.GetMediaCount, $"{MediaInfoColumns.Id}='{mediaId}'") == 0)
475 CommandHelper.Delete(Interop.MediaInfo.Delete, mediaId);
480 /// Adds the media to the database.
482 /// <param name="path">The file path to add.</param>
483 /// <returns>The <see cref="MediaInfo"/> instance that contains the record information in the database.</returns>
485 /// If the media already exists in the database, it returns the existing information.\n
487 /// The <see cref="MediaDatabase.ScanFile(string)"/> or the <see cref="MediaDatabase.ScanFolderAsync(string)"/> can be used instead.\n
489 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.\n
490 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
492 /// <privilege>http://tizen.org/privilege/content.write</privilege>
493 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
494 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
495 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
496 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
497 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
498 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
499 /// <exception cref="ArgumentException">
500 /// <paramref name="path"/> is a zero-length string, contains only white space.\n
502 /// <paramref name="path"/> contains a hidden path that starts with '.'.\n
504 /// <paramref name="path"/> contains a directory containing the ".scan_ignore" file.
506 /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exists.</exception>
507 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
508 public MediaInfo Add(string path)
512 ValidationUtil.ValidateNotNullOrEmpty(path, nameof(path));
514 if (File.Exists(path) == false)
516 throw new FileNotFoundException("destination is not valid path.", path);
519 if (File.GetAttributes(path).HasFlag(FileAttributes.Hidden))
521 throw new ArgumentException($"{nameof(path)} contains a hidden path.", nameof(path));
524 Interop.MediaInfoHandle handle = null;
528 Interop.MediaInfo.Insert(path, out handle).ThrowIfError("Failed to insert");
530 return MediaInfo.FromHandle(handle);
541 private static void ValidatePaths(IEnumerable<string> paths)
545 throw new ArgumentNullException(nameof(paths));
548 if (paths.Count() > 300)
550 throw new ArgumentException("Too many paths to add.");
553 foreach (var path in paths)
557 throw new ArgumentException($"{nameof(paths)} contains null.", nameof(paths));
560 if (File.Exists(path) == false)
562 throw new FileNotFoundException($"{nameof(paths)} contains a path that does not exist. Path={path}.", path);
569 /// Adds media files into the media database.
572 /// The paths that already exist in the database will be ignored.\n
573 /// At most 300 items can be added at once.\n
575 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.\n
576 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
578 /// <privilege>http://tizen.org/privilege/content.write</privilege>
579 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
580 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
581 /// <param name="paths">The paths of the media files to add.</param>
582 /// <returns>A task that represents the asynchronous add operation.</returns>
583 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
584 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
585 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
586 /// <exception cref="ArgumentNullException"><paramref name="paths"/> is null.</exception>
587 /// <exception cref="ArgumentException">
588 /// <paramref name="paths"/> contains null.\n
590 /// <paramref name="paths"/> contains the invalid path.\n
592 /// The number of <paramref name="paths"/> is 300 or more items.
594 /// <exception cref="FileNotFoundException"><paramref name="paths"/> contains a path that does not exist.</exception>
595 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
596 public async Task AddAsync(IEnumerable<string> paths)
600 ValidatePaths(paths);
602 var pathArray = paths.ToArray();
603 var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
605 Interop.MediaInfo.InsertCompletedCallback callback = (error, _) =>
607 if (error == MediaContentError.None)
609 tcs.TrySetResult(true);
613 tcs.TrySetException(error.AsException("Failed to add"));
617 using (ObjectKeeper.Get(callback))
619 Interop.MediaInfo.BatchInsert(pathArray, pathArray.Length, callback).ThrowIfError("Failed to add");
625 private static void SetUpdateValue<T>(Interop.MediaInfoHandle handle, T value,
626 Func<Interop.MediaInfoHandle, T, MediaContentError> func)
630 func(handle, value).ThrowIfError("Failed to update");
634 private static void SetUpdateValue<T>(Interop.MediaInfoHandle handle, Nullable<T> value,
635 Func<Interop.MediaInfoHandle, T, MediaContentError> func) where T : struct
639 func(handle, value.Value).ThrowIfError("Failed to update");
644 /// Updates the media with the specified values.
646 /// <privilege>http://tizen.org/privilege/content.write</privilege>
647 /// <param name="mediaId">The media ID to update.</param>
648 /// <param name="values">The values for update.</param>
649 /// <returns>true if the matched record was found and updated, otherwise false.</returns>
650 /// <remarks>Only values set in the <see cref="MediaInfoUpdateValues"/> are updated.</remarks>
651 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
652 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
653 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
654 /// <exception cref="ArgumentNullException">
655 /// <paramref name="mediaId"/> is null.\n
657 /// <paramref name="values"/> is null.
659 /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
660 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
661 public bool Update(string mediaId, MediaInfoUpdateValues values)
665 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
669 throw new ArgumentNullException(nameof(values));
672 if (CommandHelper.Count(
673 Interop.MediaInfo.GetMediaCount, $"{MediaInfoColumns.Id}='{mediaId}'") == 0)
678 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to update");
680 if (handle.IsInvalid)
687 SetUpdateValue(handle, values.Weather, Interop.MediaInfo.SetWeather);
688 SetUpdateValue(handle, values.IsFavorite, Interop.MediaInfo.SetFavorite);
689 SetUpdateValue(handle, values.Provider, Interop.MediaInfo.SetProvider);
690 SetUpdateValue(handle, values.Category, Interop.MediaInfo.SetCategory);
691 SetUpdateValue(handle, values.LocationTag, Interop.MediaInfo.SetLocationTag);
692 SetUpdateValue(handle, values.AgeRating, Interop.MediaInfo.SetAgeRating);
694 Interop.MediaInfo.UpdateToDB(handle).ThrowIfError("Failed to update");
704 /// Updates the path of the media to the specified destination path in the database.
706 /// <privilege>http://tizen.org/privilege/content.write</privilege>
707 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
708 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
709 /// <param name="mediaId">The media ID to move.</param>
710 /// <param name="newPath">The path that the media has been moved to.</param>
711 /// <returns>true if the matched record was found and updated, otherwise false.</returns>
713 /// Usually, it is used after the media file is moved to the another path.\n
715 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage.\n
716 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
718 /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
719 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
720 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
721 /// <exception cref="ArgumentNullException">
722 /// <paramref name="mediaId"/> is null.\n
724 /// <paramref name="newPath"/> is null.
726 /// <exception cref="ArgumentException">
727 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.\n
729 /// <paramref name="newPath"/> is a zero-length string, contains only white space.\n
731 /// <paramref name="newPath"/> contains a hidden directory that starts with '.'.\n
733 /// <paramref name="newPath"/> contains a directory containing the ".scan_ignore" file.
735 /// <exception cref="FileNotFoundException"><paramref name="newPath"/> does not exists.</exception>
736 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
737 public bool Move(string mediaId, string newPath)
741 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
743 ValidationUtil.ValidateNotNullOrEmpty(newPath, nameof(newPath));
745 if (File.Exists(newPath) == false)
747 throw new FileNotFoundException("destination is not valid path.", newPath);
750 if (File.GetAttributes(newPath).HasFlag(FileAttributes.Hidden))
752 throw new ArgumentException($"{nameof(newPath)} contains a hidden path.", nameof(newPath));
755 //TODO can be improved if MoveToDB supports result value.
756 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).
757 Ignore(MediaContentError.InvalidParameter).ThrowIfError("Failed to move");
759 if (handle.IsInvalid)
766 Interop.MediaInfo.MoveToDB(handle, newPath).ThrowIfError("Failed to move");
776 #region CreateThumbnailAsync
778 /// Creates the thumbnail image for the given media.
779 /// If the thumbnail already exists for the given media, the existing path will be returned.
781 /// <privilege>http://tizen.org/privilege/content.write</privilege>
782 /// <param name="mediaId">The media ID to create the thumbnail.</param>
783 /// <returns>A task that represents the asynchronous operation. The task result contains the thumbnail path.</returns>
784 /// <exception cref="InvalidOperationException">
785 /// The <see cref="MediaDatabase"/> is disconnected.\n
787 /// An internal error occurred while executing.
789 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
790 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
791 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
792 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
793 /// <exception cref="ArgumentException">
794 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
796 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
797 /// <exception cref="UnsupportedContentException">
798 /// The thumbnail is not available for the given media.\n
800 /// The media is in the external USB storage (<see cref="MediaInfo.StorageType"/> is <see cref="StorageType.ExternalUsb"/>).
802 public Task<string> CreateThumbnailAsync(string mediaId)
804 return CreateThumbnailAsync(mediaId, CancellationToken.None);
808 /// Creates the thumbnail image for the given media.
809 /// If the thumbnail already exists for the given media, the existing path will be returned.
811 /// <privilege>http://tizen.org/privilege/content.write</privilege>
812 /// <param name="mediaId">The media ID to create the thumbnail.</param>
813 /// <param name="cancellationToken">The token to cancel the operation.</param>
814 /// <returns>A task that represents the asynchronous operation. The task result contains the thumbnail path.</returns>
815 /// <exception cref="InvalidOperationException">
816 /// The <see cref="MediaDatabase"/> is disconnected.\n
818 /// An internal error occurred while executing.
820 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
821 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
822 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
823 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
824 /// <exception cref="ArgumentException">
825 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
827 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
828 /// <exception cref="UnsupportedContentException">
829 /// The thumbnail is not available for the given media.\n
831 /// The media is in the external USB storage (<see cref="MediaInfo.StorageType"/> is <see cref="StorageType.ExternalUsb"/>).
833 public Task<string> CreateThumbnailAsync(string mediaId, CancellationToken cancellationToken)
837 return cancellationToken.IsCancellationRequested ? Task.FromCanceled<string>(cancellationToken) :
838 CreateThumbnailAsyncCore(mediaId, cancellationToken);
841 private async Task<string> CreateThumbnailAsyncCore(string mediaId, CancellationToken cancellationToken)
843 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
845 var tcs = new TaskCompletionSource<string>();
847 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to create thumbnail");
849 if (handle.IsInvalid)
851 throw new RecordNotFoundException("Media does not exist.");
856 if (InteropHelper.GetValue<StorageType>(handle, Interop.MediaInfo.GetStorageType) == StorageType.ExternalUsb)
858 throw new UnsupportedContentException("The media is in external usb storage.");
861 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
863 if (File.Exists(path) == false)
865 throw new FileNotFoundException($"The media file does not exist. Path={path}.", path);
868 using (RegisterCancelThumbnail(cancellationToken, tcs, handle))
869 using (var cbKeeper = ObjectKeeper.Get(GetCreateThumbnailCallback(tcs)))
871 Interop.MediaInfo.CreateThumbnail(handle, cbKeeper.Target).ThrowIfError("Failed to create thumbnail");
873 return await tcs.Task;
878 private static Interop.MediaInfo.ThumbnailCompletedCallback GetCreateThumbnailCallback(
879 TaskCompletionSource<string> tcs)
881 return (error, path, _) =>
883 if (error != MediaContentError.None)
885 tcs.TrySetException(error.AsException("Failed to create thumbnail"));
889 tcs.TrySetResult(path);
894 private static IDisposable RegisterCancelThumbnail(CancellationToken cancellationToken,
895 TaskCompletionSource<string> tcs, Interop.MediaInfoHandle handle)
897 if (cancellationToken.CanBeCanceled == false)
902 return cancellationToken.Register(() =>
904 if (tcs.Task.IsCompleted)
909 Interop.MediaInfo.CancelThumbnail(handle).ThrowIfError("Failed to cancel");
910 tcs.TrySetCanceled();
915 #region DetectFaceAsync
917 /// Detects faces from the given media.
918 /// If the thumbnail already exists for the given media, the existing path will be returned.
920 /// <privilege>http://tizen.org/privilege/content.write</privilege>
921 /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
922 /// <param name="mediaId">The media ID to create the thumbnail.</param>
923 /// <returns>A task that represents the asynchronous add operation. The task result contains the number of faces detected.</returns>
924 /// <exception cref="InvalidOperationException">
925 /// The <see cref="MediaDatabase"/> is disconnected.\n
927 /// An internal error occurred while executing.
929 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
930 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
931 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
932 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
933 /// <exception cref="ArgumentException">
934 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
936 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
937 /// <exception cref="UnsupportedContentException">Face detection is not available for the given media.</exception>
938 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
939 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
940 public Task<int> DetectFaceAsync(string mediaId)
942 return DetectFaceAsync(mediaId, CancellationToken.None);
946 /// Creates the thumbnail image for the given media.
947 /// If the thumbnail already exists for the given media, the existing path will be returned.
950 /// Media in the external storage is not supported, with the exception of MMC.
952 /// <privilege>http://tizen.org/privilege/content.write</privilege>
953 /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
954 /// <param name="mediaId">The media ID to create the thumbnail.</param>
955 /// <param name="cancellationToken">The token to cancel the operation.</param>
956 /// <returns>A task that represents the asynchronous operation. The task result contains the number of faces detected.</returns>
957 /// <exception cref="InvalidOperationException">
958 /// The <see cref="MediaDatabase"/> is disconnected.\n
960 /// An internal error occurred while executing.
962 /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
963 /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
964 /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
965 /// <exception cref="RecordNotFoundException"><paramref name="mediaId"/> does not exist in the database.</exception>
966 /// <exception cref="ArgumentException">
967 /// <paramref name="mediaId"/> is a zero-length string, contains only white space.
969 /// <exception cref="FileNotFoundException">The file of the media does not exists; moved or deleted.</exception>
970 /// <exception cref="UnsupportedContentException">
971 /// Face detection is not available for the given media.\n
973 /// The media is in the external USB storage (<see cref="MediaInfo.StorageType"/> is <see cref="StorageType.ExternalUsb"/>).
975 /// <exception cref="NotSupportedException">The required feature is not supported.</exception>
976 public Task<int> DetectFaceAsync(string mediaId, CancellationToken cancellationToken)
978 if (Features.IsSupported(Features.FaceRecognition) == false)
980 throw new NotSupportedException($"The feature({Features.FaceRecognition}) is not supported.");
985 return cancellationToken.IsCancellationRequested ? Task.FromCanceled<int>(cancellationToken) :
986 DetectFaceAsyncCore(mediaId, cancellationToken);
989 private static async Task<int> DetectFaceAsyncCore(string mediaId, CancellationToken cancellationToken)
991 ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
993 var tcs = new TaskCompletionSource<int>();
995 Interop.MediaInfo.GetMediaFromDB(mediaId, out var handle).ThrowIfError("Failed to detect faces");
997 if (handle.IsInvalid)
999 throw new RecordNotFoundException("Media does not exist.");
1004 if (InteropHelper.GetValue<StorageType>(handle, Interop.MediaInfo.GetStorageType) == StorageType.ExternalUsb)
1006 throw new UnsupportedContentException("The media is in external usb storage.");
1009 if (InteropHelper.GetValue<MediaType>(handle, Interop.MediaInfo.GetMediaType) != MediaType.Image)
1011 throw new UnsupportedContentException("Only image is supported.");
1014 var path = InteropHelper.GetString(handle, Interop.MediaInfo.GetFilePath);
1016 if (File.Exists(path) == false)
1018 throw new FileNotFoundException($"The media file does not exist. Path={path}.", path);
1021 using (RegisterCancelFaceDetection(cancellationToken, tcs, handle))
1022 using (var cbKeeper = ObjectKeeper.Get(GetFaceDetectionCallback(tcs)))
1024 Interop.MediaInfo.StartFaceDetection(handle, cbKeeper.Target).ThrowIfError("Failed to detect faces");
1026 return await tcs.Task;
1031 private static Interop.MediaInfo.FaceDetectionCompletedCallback GetFaceDetectionCallback(
1032 TaskCompletionSource<int> tcs)
1034 return (error, count, _) =>
1036 if (error != MediaContentError.None)
1038 tcs.TrySetException(error.AsException("Failed to detect faces"));
1042 tcs.TrySetResult(count);
1047 private static IDisposable RegisterCancelFaceDetection(CancellationToken cancellationToken,
1048 TaskCompletionSource<int> tcs, Interop.MediaInfoHandle handle)
1050 if (cancellationToken.CanBeCanceled == false)
1055 return cancellationToken.Register(() =>
1057 if (tcs.Task.IsCompleted)
1062 Interop.MediaInfo.CancelFaceDetection(handle).ThrowIfError("Failed to cancel");
1063 tcs.TrySetCanceled();