Release 4.0.0-preview1-00172
[platform/core/csapi/tizenfx.git] / src / Tizen.Content.MediaContent / Tizen.Content.MediaContent / PlaylistCommand.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 using System;
18 using System.Collections.Generic;
19 using System.IO;
20 using System.Linq;
21
22 namespace Tizen.Content.MediaContent
23 {
24     /// <summary>
25     /// Provides the commands to manage playlists in the database.
26     /// </summary>
27     /// <seealso cref="Playlist"/>
28     public class PlaylistCommand : MediaCommand
29     {
30         /// <summary>
31         /// Initializes a new instance of the <see cref="PlaylistCommand"/> class with the specified <see cref="MediaDatabase"/>.
32         /// </summary>
33         /// <param name="database">A <see cref="MediaDatabase"/> that the commands run on.</param>
34         /// <exception cref="ArgumentNullException"><paramref name="database"/> is null.</exception>
35         /// <exception cref="ObjectDisposedException"><paramref name="database"/> has already been disposed of.</exception>
36         public PlaylistCommand(MediaDatabase database) : base(database)
37         {
38         }
39
40         /// <summary>
41         /// Retrieves the number of playlists.
42         /// </summary>
43         /// <returns>The number of playlists.</returns>
44         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
45         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
46         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
47         public int Count()
48         {
49             return Count(null);
50         }
51
52         /// <summary>
53         /// Retrieves the number of playlists with the <see cref="CountArguments"/>.
54         /// </summary>
55         /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
56         /// <returns>The number of playlists.</returns>
57         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
58         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
59         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
60         public int Count(CountArguments arguments)
61         {
62             ValidateDatabase();
63
64             return CommandHelper.Count(Interop.Playlist.GetPlaylistCount, arguments);
65         }
66
67         /// <summary>
68         /// Retrieves the play order of the member.
69         /// </summary>
70         /// <param name="playlistId">The playlist ID.</param>
71         /// <param name="memberId">The member ID of the playlist.</param>
72         /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
73         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
74         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
75         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
76         /// <exception cref="ArgumentOutOfRangeException">
77         ///     <paramref name="playlistId"/> is less than or equal to zero.\n
78         ///     -or-\n
79         ///     <paramref name="memberId"/> is less than or equal to zero.
80         /// </exception>
81         public int GetPlayOrder(int playlistId, int memberId)
82         {
83             ValidateDatabase();
84
85             if (playlistId <= 0)
86             {
87                 throw new ArgumentOutOfRangeException(nameof(playlistId), playlistId,
88                     "Playlist id can't be less than or equal to zero.");
89             }
90
91             if (memberId <= 0)
92             {
93                 throw new ArgumentOutOfRangeException(nameof(memberId), memberId,
94                     "Member id can't be less than or equal to zero.");
95             }
96
97             Interop.Playlist.GetPlayOrder(playlistId, memberId, out var order).ThrowIfError("Failed to query");
98
99             return order;
100         }
101
102         /// <summary>
103         /// Deletes a playlist from the database.
104         /// </summary>
105         /// <privilege>http://tizen.org/privilege/content.write</privilege>
106         /// <param name="playlistId">The playlist ID to delete.</param>
107         /// <returns>true if the matched record was found and deleted, otherwise false.</returns>
108         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
109         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
110         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
111         /// <exception cref="ArgumentOutOfRangeException"><paramref name="playlistId"/> is less than or equal to zero.</exception>
112         /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
113         public bool Delete(int playlistId)
114         {
115             ValidateDatabase();
116
117             if (playlistId <= 0)
118             {
119                 throw new ArgumentOutOfRangeException(nameof(playlistId), playlistId,
120                     "Playlist id can't be less than or equal to zero.");
121             }
122
123             if (Select(playlistId) == null)
124             {
125                 return false;
126             }
127
128             CommandHelper.Delete(Interop.Playlist.Delete, playlistId);
129             return true;
130         }
131
132         /// <summary>
133         /// Inserts the playlist into the database from the specified M3U file.
134         /// </summary>
135         /// <remarks>
136         ///     If you want to access an internal storage, you should add privilege http://tizen.org/privilege/mediastorage.\n
137         ///     If you want to access an external storage, you should add privilege http://tizen.org/privilege/externalstorage.
138         /// </remarks>
139         /// <privilege>http://tizen.org/privilege/content.write</privilege>
140         /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
141         /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
142         /// <param name="name">The name of the playlist.</param>
143         /// <param name="path">The path to a M3U file to import.</param>
144         /// <returns>The <see cref="Playlist"/> instance that contains the record information inserted.</returns>
145         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
146         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
147         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
148         /// <exception cref="ArgumentNullException">
149         ///     <paramref name="name"/> is null.\n
150         ///     -or-\n
151         ///     <paramref name="path"/> is null.
152         /// </exception>
153         /// <exception cref="ArgumentException">
154         ///     <paramref name="name"/> is a zero-length string.\n
155         ///     -or-\n
156         ///     <paramref name="path"/> is a zero-length string, contains only white space.
157         /// </exception>
158         /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exists.</exception>
159         /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
160         public Playlist InsertFromFile(string name, string path)
161         {
162             ValidateDatabase();
163
164             if (name == null)
165             {
166                 throw new ArgumentNullException(nameof(name));
167             }
168
169             if (name.Length == 0)
170             {
171                 throw new ArgumentException("Playlist name can't be an empty string.");
172             }
173
174             ValidationUtil.ValidateNotNullOrEmpty(path, nameof(path));
175
176             if (File.Exists(path) == false)
177             {
178                 throw new FileNotFoundException("The specified path does not exists.", path);
179             }
180
181             IntPtr handle = IntPtr.Zero;
182             Interop.Playlist.ImportFromFile(path, name, out handle).ThrowIfError("Failed to insert");
183
184             try
185             {
186                 return new Playlist(handle);
187             }
188             finally
189             {
190                 if (handle != IntPtr.Zero)
191                 {
192                     Interop.Playlist.Destroy(handle);
193                 }
194             }
195         }
196         /// <summary>
197         /// Exports the playlist to a M3U file.
198         /// </summary>
199         /// <remarks>
200         ///     If the file already exists in the file system, then it will be overwritten.\n
201         ///     \n
202         ///     If you want to access an internal storage, you should add privilege http://tizen.org/privilege/mediastorage.\n
203         ///     If you want to access an external storage, you should add privilege http://tizen.org/privilege/externalstorage.
204         /// </remarks>
205         /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
206         /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
207         /// <param name="playlistId">The playlist ID to export.</param>
208         /// <param name="path">The path to a M3U file.</param>
209         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
210         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
211         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
212         /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
213         /// <exception cref="ArgumentException"><paramref name="path"/> is a zero-length string, contains only white space.</exception>
214         /// <exception cref="ArgumentOutOfRangeException"><paramref name="playlistId"/> is less than or equal to zero.</exception>
215         /// <exception cref="RecordNotFoundException">No matching playlist exists.</exception>
216         /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
217         public void ExportToFile(int playlistId, string path)
218         {
219             ValidateDatabase();
220
221             ValidationUtil.ValidateNotNullOrEmpty(path, nameof(path));
222
223             if (playlistId <= 0)
224             {
225                 throw new ArgumentOutOfRangeException(nameof(playlistId), playlistId,
226                     "Playlist id can't be less than or equal to zero.");
227             }
228
229             IntPtr handle = IntPtr.Zero;
230             try
231             {
232                 Interop.Playlist.GetPlaylistFromDb(playlistId, out handle).ThrowIfError("Failed to query");
233
234                 if (handle == IntPtr.Zero)
235                 {
236                     throw new RecordNotFoundException("No matching playlist exists.");
237                 }
238
239                 Interop.Playlist.ExportToFile(handle, path).ThrowIfError("Failed to export");
240             }
241             finally
242             {
243                 if (handle != IntPtr.Zero)
244                 {
245                     Interop.Playlist.Destroy(handle);
246                 }
247             }
248         }
249
250         /// <summary>
251         /// Inserts the playlist into the database with the specified name.
252         /// </summary>
253         /// <privilege>http://tizen.org/privilege/content.write</privilege>
254         /// <param name="name">The name of the playlist.</param>
255         /// <returns>The <see cref="Playlist"/> instance that contains the record information inserted.</returns>
256         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
257         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
258         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
259         /// <exception cref="ArgumentNullException"><paramref name="name"/> is null.</exception>
260         /// <exception cref="ArgumentException"><paramref name="name"/> is a zero-length string.</exception>
261         /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
262         public Playlist Insert(string name)
263         {
264             return Insert(name, null);
265         }
266
267         /// <summary>
268         /// Inserts the playlist into the database with the specified name and the thumbnail path.
269         /// </summary>
270         /// <privilege>http://tizen.org/privilege/content.write</privilege>
271         /// <param name="name">The name of the playlist.</param>
272         /// <param name="thumbnailPath">The path of the thumbnail for the playlist. This value can be null.</param>
273         /// <returns>The <see cref="Playlist"/> instance that contains the record information inserted.</returns>
274         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
275         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
276         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
277         /// <exception cref="ArgumentNullException"><paramref name="name"/> is null.</exception>
278         /// <exception cref="ArgumentException"><paramref name="name"/> is a zero-length string.</exception>
279         /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
280         public Playlist Insert(string name, string thumbnailPath)
281         {
282             ValidateDatabase();
283
284             if (name == null)
285             {
286                 throw new ArgumentNullException(nameof(name));
287             }
288
289             if (name.Length == 0)
290             {
291                 throw new ArgumentException("Playlist name can't be an empty string.");
292             }
293
294             IntPtr handle = IntPtr.Zero;
295             Interop.Playlist.Create(out handle).ThrowIfError("Failed to insert");
296
297             try
298             {
299                 Interop.Playlist.SetName(handle, name).ThrowIfError("Failed to insert");
300
301                 if (thumbnailPath != null)
302                 {
303                     Interop.Playlist.SetThumbnailPath(handle, thumbnailPath).ThrowIfError("Failed to insert");
304                 }
305
306                 Interop.Playlist.Insert(handle).ThrowIfError("Failed to insert");
307                 return new Playlist(handle);
308             }
309             finally
310             {
311                 if (handle != IntPtr.Zero)
312                 {
313                     Interop.Playlist.Destroy(handle);
314                 }
315             }
316         }
317
318         /// <summary>
319         /// Retrieves the playlists.
320         /// </summary>
321         /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
322         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
323         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
324         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
325         public MediaDataReader<Playlist> Select()
326         {
327             return Select(null);
328         }
329
330         /// <summary>
331         /// Retrieves the playlists with the <see cref="SelectArguments"/>.
332         /// </summary>
333         /// <param name="filter">The criteria to use to filter. This value can be null.</param>
334         /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
335         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
336         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
337         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
338         public MediaDataReader<Playlist> Select(SelectArguments filter)
339         {
340             ValidateDatabase();
341
342             return CommandHelper.Select(filter, Interop.Playlist.ForeachPlaylistFromDb,
343                 Playlist.FromHandle);
344         }
345
346         /// <summary>
347         /// Retrieves the playlist with the specified playlist ID.
348         /// </summary>
349         /// <param name="playlistId">The playlist ID to select.</param>
350         /// <returns>The <see cref="Playlist"/> instance if the matched record was found in the database, otherwise null.</returns>
351         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
352         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
353         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
354         /// <exception cref="ArgumentOutOfRangeException"><paramref name="playlistId"/> is less than or equal to zero.</exception>
355         public Playlist Select(int playlistId)
356         {
357             ValidateDatabase();
358
359             if (playlistId <= 0)
360             {
361                 throw new ArgumentOutOfRangeException(nameof(playlistId), playlistId,
362                     "Playlist id can't be less than or equal to zero.");
363             }
364
365             IntPtr handle = IntPtr.Zero;
366
367             try
368             {
369                 Interop.Playlist.GetPlaylistFromDb(playlistId, out handle).ThrowIfError("Failed to query");
370
371                 if (handle == IntPtr.Zero)
372                 {
373                     return null;
374                 }
375
376                 return new Playlist(handle);
377             }
378             finally
379             {
380                 if (handle != IntPtr.Zero)
381                 {
382                     Interop.Playlist.Destroy(handle);
383                 }
384             }
385         }
386
387         /// <summary>
388         /// Retrieves the number of media information of the playlist.
389         /// </summary>
390         /// <param name="playlistId">The playlist ID to count media added to the playlist.</param>
391         /// <returns>The number of media information.</returns>
392         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
393         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
394         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
395         /// <exception cref="ArgumentOutOfRangeException"><paramref name="playlistId"/> is less than or equal to zero.</exception>
396         public int CountMember(int playlistId)
397         {
398             return CountMember(playlistId, null);
399         }
400
401         /// <summary>
402         /// Retrieves the number of media information of the playlist with the <see cref="CountArguments"/>.
403         /// </summary>
404         /// <param name="playlistId">The playlist ID to count the media added to the playlist.</param>
405         /// <param name="arguments">The criteria to use to filter. This value can be null.</param>
406         /// <returns>The number of media information.</returns>
407         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
408         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
409         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
410         /// <exception cref="ArgumentOutOfRangeException"><paramref name="playlistId"/> is less than or equal to zero.</exception>
411         public int CountMember(int playlistId, CountArguments arguments)
412         {
413             ValidateDatabase();
414
415             if (playlistId <= 0)
416             {
417                 throw new ArgumentOutOfRangeException(nameof(playlistId), playlistId,
418                     "Playlist id can't be less than or equal to zero.");
419             }
420
421             return CommandHelper.Count(Interop.Playlist.GetMediaCountFromDb, playlistId, arguments);
422
423         }
424
425         private static List<PlaylistMember> GetMembers(int playlistId, SelectArguments arguments)
426         {
427             using (var filter = QueryArguments.ToNativeHandle(arguments))
428             {
429                 Exception caught = null;
430                 List<PlaylistMember> list = new List<PlaylistMember>();
431
432                 Interop.Playlist.ForeachMediaFromDb(playlistId, filter, (memberId, mediaInfoHandle, _) =>
433                 {
434                     try
435                     {
436                         list.Add(new PlaylistMember(memberId, MediaInfo.FromHandle(mediaInfoHandle)));
437
438                         return true;
439                     }
440                     catch (Exception e)
441                     {
442                         caught = e;
443                         return false;
444                     }
445                 }).ThrowIfError("Failed to query");
446
447                 if (caught != null)
448                 {
449                     throw caught;
450                 }
451
452                 return list;
453             }
454         }
455
456         /// <summary>
457         /// Retrieves the member ID of the media in the playlist.
458         /// </summary>
459         /// <param name="playlistId">The playlist ID.</param>
460         /// <param name="mediaId">The media ID.</param>
461         /// <returns>The member ID if the member was found in the playlist, otherwise -1.</returns>
462         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
463         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
464         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
465         /// <exception cref="ArgumentOutOfRangeException"><paramref name="playlistId"/> is less than or equal to zero.</exception>
466         /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
467         /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
468         public int GetMemberId(int playlistId, string mediaId)
469         {
470             ValidateDatabase();
471
472             ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
473
474             if (playlistId <= 0)
475             {
476                 throw new ArgumentOutOfRangeException(nameof(playlistId), playlistId,
477                     "Playlist id can't be less than or equal to zero.");
478             }
479
480             var reader = SelectMember(playlistId, new SelectArguments()
481             {
482                 FilterExpression = $"{MediaInfoColumns.Id}='{mediaId}'"
483             });
484             reader.Read();
485
486             return reader.Current?.MemberId ?? -1;
487         }
488
489         /// <summary>
490         /// Retrieves the media information of the playlist.
491         /// </summary>
492         /// <param name="playlistId">The playlist ID to query with.</param>
493         /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
494         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
495         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
496         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
497         /// <exception cref="ArgumentOutOfRangeException"><paramref name="playlistId"/> is less than or equal to zero.</exception>
498         public MediaDataReader<PlaylistMember> SelectMember(int playlistId)
499         {
500             return SelectMember(playlistId, null);
501         }
502
503         /// <summary>
504         /// Retrieves the media information of the playlist with the <see cref="SelectArguments"/>.
505         /// </summary>
506         /// <param name="playlistId">The playlist ID to query with.</param>
507         /// <param name="filter">The criteria to use to filter. This value can be null.</param>
508         /// <returns>The <see cref="MediaDataReader{TRecord}"/> containing the results.</returns>
509         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
510         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
511         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
512         /// <exception cref="ArgumentOutOfRangeException"><paramref name="playlistId"/> is less than or equal to zero.</exception>
513         public MediaDataReader<PlaylistMember> SelectMember(int playlistId, SelectArguments filter)
514         {
515             ValidateDatabase();
516
517             if (playlistId <= 0)
518             {
519                 throw new ArgumentOutOfRangeException(nameof(playlistId), playlistId,
520                     "Playlist id can't be less than or equal to zero.");
521             }
522
523             return new MediaDataReader<PlaylistMember>(GetMembers(playlistId, filter));
524         }
525
526         /// <summary>
527         /// Updates the playlist with the specified values.
528         /// </summary>
529         /// <privilege>http://tizen.org/privilege/content.write</privilege>
530         /// <param name="playlistId">The playlist ID to update.</param>
531         /// <param name="values">The values for the update.</param>
532         /// <returns>true if the matched record was found and updated, otherwise false.</returns>
533         /// <remarks>Only values set in the <see cref="PlaylistUpdateValues"/> are updated.</remarks>
534         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
535         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
536         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
537         /// <exception cref="ArgumentNullException"><paramref name="values"/> is null.</exception>
538         /// <exception cref="ArgumentOutOfRangeException"><paramref name="playlistId"/> is less than or equal to zero.</exception>
539         /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
540         public bool Update(int playlistId, PlaylistUpdateValues values)
541         {
542             ValidateDatabase();
543
544             if (playlistId <= 0)
545             {
546                 throw new ArgumentOutOfRangeException(nameof(playlistId), playlistId,
547                     "Playlist id can't be less than or equal to zero.");
548             }
549
550             if (values == null)
551             {
552                 throw new ArgumentNullException(nameof(values));
553             }
554
555             if (CommandHelper.Count(
556                 Interop.Playlist.GetPlaylistCount, $"{PlaylistColumns.Id}={playlistId}") == 0)
557             {
558                 return false;
559             }
560
561             Interop.Playlist.Create(out var handle).ThrowIfError("Failed to update");
562
563             try
564             {
565                 if (values.Name != null)
566                 {
567                     Interop.Playlist.SetName(handle, values.Name).ThrowIfError("Failed to update");
568                 }
569
570                 if (values.ThumbnailPath != null)
571                 {
572                     Interop.Playlist.SetThumbnailPath(handle, values.ThumbnailPath).ThrowIfError("Failed to update");
573                 }
574
575                 Interop.Playlist.Update(playlistId, handle).ThrowIfError("Failed to update");
576                 return true;
577             }
578             finally
579             {
580                 if (handle != IntPtr.Zero)
581                 {
582                     Interop.Playlist.Destroy(handle);
583                 }
584             }
585         }
586
587         /// <summary>
588         /// Adds the media to the playlist.
589         /// </summary>
590         /// <param name="playlistId">The playlist ID that the media will be added to.</param>
591         /// <param name="mediaId">The media ID to add to the playlist.</param>
592         /// <returns>true if the matched record was found and updated, otherwise false.</returns>
593         /// <remarks>The invalid media ID will be ignored.</remarks>
594         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
595         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
596         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
597         /// <exception cref="ArgumentNullException"><paramref name="mediaId"/> is null.</exception>
598         /// <exception cref="ArgumentException"><paramref name="mediaId"/> is a zero-length string, contains only white space.</exception>
599         /// <exception cref="ArgumentOutOfRangeException"><paramref name="playlistId"/> is less than or equal to zero.</exception>
600         public bool AddMember(int playlistId, string mediaId)
601         {
602             ValidationUtil.ValidateNotNullOrEmpty(mediaId, nameof(mediaId));
603
604             return AddMembers(playlistId, new string[] { mediaId });
605         }
606
607         /// <summary>
608         /// Adds the media set to the playlist.
609         /// </summary>
610         /// <param name="playlistId">The playlist ID that the media will be added to.</param>
611         /// <param name="mediaIds">The collection of media ID to add to the playlist.</param>
612         /// <returns>true if the matched record was found and updated, otherwise false.</returns>
613         /// <remarks>The invalid media IDs will be ignored.</remarks>
614         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
615         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
616         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
617         /// <exception cref="ArgumentNullException"><paramref name="mediaIds"/> is null.</exception>
618         /// <exception cref="ArgumentException">
619         ///     <paramref name="mediaIds"/> has no element.\n
620         ///     -or-\n
621         ///     <paramref name="mediaIds"/> contains null value.\n
622         ///     -or-\n
623         ///     <paramref name="mediaIds"/> contains a zero-length string or white space.\n
624         /// </exception>
625         /// <exception cref="ArgumentOutOfRangeException"><paramref name="playlistId"/> is less than or equal to zero.</exception>
626         public bool AddMembers(int playlistId, IEnumerable<string> mediaIds)
627         {
628             ValidateDatabase();
629
630             if (playlistId <= 0)
631             {
632                 throw new ArgumentOutOfRangeException(nameof(playlistId), playlistId,
633                     "Playlist id can't be less than or equal to zero.");
634             }
635
636             if (mediaIds == null)
637             {
638                 throw new ArgumentNullException(nameof(mediaIds));
639             }
640
641             if (mediaIds.Count() == 0)
642             {
643                 throw new ArgumentException("mediaIds has no element.", nameof(mediaIds));
644             }
645
646             if (CommandHelper.Count(
647                 Interop.Playlist.GetPlaylistCount, $"{PlaylistColumns.Id}={playlistId}") == 0)
648             {
649                 return false;
650             }
651
652             IntPtr handle = IntPtr.Zero;
653             Interop.Playlist.Create(out handle).ThrowIfError("Failed to add member");
654
655             try
656             {
657                 foreach (var mediaId in mediaIds)
658                 {
659                     if (mediaId == null)
660                     {
661                         throw new ArgumentException("Media id should not be null.", nameof(mediaIds));
662                     }
663
664                     if (string.IsNullOrWhiteSpace(mediaId))
665                     {
666                         throw new ArgumentException("Media id should not be empty.", nameof(mediaId));
667                     }
668
669                     Interop.Playlist.AddMedia(handle, mediaId).ThrowIfError("Failed to add member");
670                 }
671
672                 Interop.Playlist.Update(playlistId, handle).ThrowIfError("Failed to add member");
673                 return true;
674             }
675             finally
676             {
677                 if (handle != IntPtr.Zero)
678                 {
679                     Interop.Playlist.Destroy(handle);
680                 }
681             }
682         }
683
684         /// <summary>
685         /// Removes a member from the playlist.
686         /// </summary>
687         /// <param name="playlistId">The playlist ID.</param>
688         /// <param name="memberId">The member ID to be removed.</param>
689         /// <returns>true if the matched record was found and updated, otherwise false.</returns>
690         /// <remarks>The invalid ID will be ignored.</remarks>
691         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
692         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
693         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
694         /// <exception cref="ArgumentOutOfRangeException">
695         ///     <paramref name="playlistId"/> is less than or equal to zero.\n
696         ///     -or-\n
697         ///     <paramref name="memberId"/> is less than or equal to zero.\n
698         /// </exception>
699         public bool RemoveMember(int playlistId, int memberId)
700         {
701             if (memberId <= 0)
702             {
703                 throw new ArgumentOutOfRangeException(nameof(memberId), memberId,
704                     "Member id can't be less than or equal to zero.");
705             }
706
707             return RemoveMembers(playlistId, new int[] { memberId });
708         }
709
710         /// <summary>
711         /// Removes a media set from the playlist.
712         /// </summary>
713         /// <param name="playlistId">The playlist ID.</param>
714         /// <param name="memberIds">The collection of member ID to remove from to the playlist.</param>
715         /// <returns>true if the matched record was found and updated, otherwise false.</returns>
716         /// <remarks>The invalid IDs will be ignored.</remarks>
717         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
718         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
719         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
720         /// <exception cref="ArgumentNullException"><paramref name="memberIds"/> is null.</exception>
721         /// <exception cref="ArgumentException">
722         ///     <paramref name="memberIds"/> has no element.\n
723         ///     -or-\n
724         ///     <paramref name="memberIds"/> contains a value which is less than or equal to zero.
725         /// </exception>
726         /// <exception cref="ArgumentOutOfRangeException"><paramref name="playlistId"/> is less than or equal to zero.</exception>
727         public bool RemoveMembers(int playlistId, IEnumerable<int> memberIds)
728         {
729             ValidateDatabase();
730
731             if (playlistId <= 0)
732             {
733                 throw new ArgumentOutOfRangeException(nameof(playlistId), playlistId,
734                     "Playlist id can't be less than or equal to zero.");
735             }
736
737             if (memberIds == null)
738             {
739                 throw new ArgumentNullException(nameof(memberIds));
740             }
741
742             if (memberIds.Count() == 0)
743             {
744                 throw new ArgumentException("memberIds has no element.", nameof(memberIds));
745             }
746
747             if (CommandHelper.Count(
748                 Interop.Playlist.GetPlaylistCount, $"{PlaylistColumns.Id}={playlistId}") == 0)
749             {
750                 return false;
751             }
752
753             IntPtr handle = IntPtr.Zero;
754             Interop.Playlist.Create(out handle).ThrowIfError("Failed to add member");
755
756             try
757             {
758                 foreach (var memberId in memberIds)
759                 {
760                     if (memberId <= 0)
761                     {
762                         throw new ArgumentException(nameof(memberIds),
763                             "Member id can't be less than or equal to zero.");
764                     }
765
766                     Interop.Playlist.RemoveMedia(handle, memberId).ThrowIfError("Failed to add member");
767                 }
768
769                 Interop.Playlist.Update(playlistId, handle).ThrowIfError("Failed to add member");
770                 return true;
771             }
772             finally
773             {
774                 if (handle != IntPtr.Zero)
775                 {
776                     Interop.Playlist.Destroy(handle);
777                 }
778             }
779         }
780
781         /// <summary>
782         /// Updates a play order of the playlist.
783         /// </summary>
784         /// <param name="playlistId">The playlist ID.</param>
785         /// <param name="playOrder">The <see cref="PlayOrder"/> to apply.</param>
786         /// <returns>true if the matched record was found and updated, otherwise false.</returns>
787         /// <remarks>The <see cref="PlayOrder.MemberId"/> that is invalid will be ignored.</remarks>
788         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
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="playOrder"/> is null.</exception>
792         /// <exception cref="ArgumentOutOfRangeException"><paramref name="playlistId"/> is less than or equal to zero.</exception>
793         public bool UpdatePlayOrder(int playlistId, PlayOrder playOrder)
794         {
795             if (playOrder == null)
796             {
797                 throw new ArgumentNullException(nameof(playOrder));
798             }
799             return UpdatePlayOrders(playlistId, new PlayOrder[] { playOrder });
800         }
801
802         /// <summary>
803         /// Updates play orders of the playlist.
804         /// </summary>
805         /// <param name="playlistId">The playlist ID.</param>
806         /// <param name="orders">The collection of the <see cref="PlayOrder"/> to apply.</param>
807         /// <returns>true if the matched record was found and updated, otherwise false.</returns>
808         /// <remarks>The <see cref="PlayOrder.MemberId"/> that is invalid will be ignored.</remarks>
809         /// <exception cref="InvalidOperationException">The <see cref="MediaDatabase"/> is disconnected.</exception>
810         /// <exception cref="ObjectDisposedException">The <see cref="MediaDatabase"/> has already been disposed of.</exception>
811         /// <exception cref="MediaDatabaseException">An error occurred while executing the command.</exception>
812         /// <exception cref="ArgumentNullException"><paramref name="orders"/> is null.</exception>
813         /// <exception cref="ArgumentException">
814         ///     <paramref name="orders"/> has no element.\n
815         ///     -or-\n
816         ///     <paramref name="orders"/> contains a null value.
817         /// </exception>
818         /// <exception cref="ArgumentOutOfRangeException"><paramref name="playlistId"/> is less than or equal to zero.</exception>
819         public bool UpdatePlayOrders(int playlistId, IEnumerable<PlayOrder> orders)
820         {
821             ValidateDatabase();
822
823             if (playlistId <= 0)
824             {
825                 throw new ArgumentOutOfRangeException(nameof(playlistId), playlistId,
826                     "Playlist id can't be less than or equal to zero.");
827             }
828
829             if (orders == null)
830             {
831                 throw new ArgumentNullException(nameof(orders));
832             }
833
834             if (orders.Count() == 0)
835             {
836                 throw new ArgumentException("memberIds has no element.", nameof(orders));
837             }
838
839             if (CommandHelper.Count(
840                 Interop.Playlist.GetPlaylistCount, $"{PlaylistColumns.Id}={playlistId}") == 0)
841             {
842                 return false;
843             }
844
845             IntPtr handle = IntPtr.Zero;
846             Interop.Playlist.Create(out handle).ThrowIfError("Failed to add member");
847
848             try
849             {
850                 foreach (var order in orders)
851                 {
852                     if (order == null)
853                     {
854                         throw new ArgumentException(nameof(orders),
855                             "orders should not contain null value.");
856                     }
857                     Interop.Playlist.SetPlayOrder(handle, order.MemberId, order.Value).ThrowIfError("Failed to add member");
858                 }
859
860                 Interop.Playlist.Update(playlistId, handle).ThrowIfError("Failed to add member");
861                 return true;
862             }
863             finally
864             {
865                 if (handle != IntPtr.Zero)
866                 {
867                     Interop.Playlist.Destroy(handle);
868                 }
869             }
870         }
871     }
872 }