[NUI] TCSACR-226 code change (#1032)
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.Metadata / MetadataEditor / MetadataEditor.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.IO;
19 using System.Runtime.InteropServices;
20
21 namespace Tizen.Multimedia
22 {
23     /// <summary>
24     /// Provides a means to edit the metadata of the media file.
25     /// </summary>
26     /// <privilege>
27     /// If you want to access only an internal storage,
28     /// you should add privilege http://tizen.org/privilege/mediastorage.<br/>
29     /// Or if you want to access only an external storage,
30     /// you should add privilege http://tizen.org/privilege/externalstorage.
31     /// </privilege>
32     /// <since_tizen> 3 </since_tizen>
33     public class MetadataEditor : IDisposable
34     {
35         private bool _disposed = false;
36         private IntPtr _handle = IntPtr.Zero;
37         private bool _isFileReadOnly;
38
39         private IntPtr Handle
40         {
41             get
42             {
43                 if (_handle == IntPtr.Zero)
44                 {
45                     throw new ObjectDisposedException(nameof(MetadataEditor));
46                 }
47
48                 return _handle;
49             }
50         }
51
52         /// <summary>
53         /// Initializes a new instance of the <see cref="MetadataEditor"/> class with the specified path.
54         /// </summary>
55         /// <since_tizen> 3 </since_tizen>
56         /// <param name="path">The path of the media file to edit the metadata.</param>
57         /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
58         /// <exception cref="ArgumentException"><paramref name="path"/> is a zero-length string, contains only white space.</exception>
59         /// <exception cref="FileFormatException">The file is not supported.</exception>
60         /// <exception cref="FileNotFoundException">The file does not exist.</exception>
61         /// <exception cref="UnauthorizedAccessException">The caller does not have required privilege to access the file.</exception>
62         public MetadataEditor(string path)
63         {
64             if (path == null)
65             {
66                 throw new ArgumentNullException(nameof(path));
67             }
68
69             if (string.IsNullOrWhiteSpace(path))
70             {
71                 throw new ArgumentException($"{nameof(path)} is a zero-length string.", nameof(path));
72             }
73
74             Interop.MetadataEditor.Create(out _handle).ThrowIfError("Failed to create metadata");
75
76             try
77             {
78                 Interop.MetadataEditor.SetPath(Handle, path).ThrowIfError("Failed to set path");
79
80                 _isFileReadOnly = File.GetAttributes(path).HasFlag(FileAttributes.ReadOnly);
81             }
82             catch (Exception)
83             {
84                 Interop.MetadataEditor.Destroy(_handle);
85                 throw;
86             }
87         }
88
89         private string GetParam(MetadataEditorAttr attr)
90         {
91             IntPtr val = IntPtr.Zero;
92
93             try
94             {
95                 Interop.MetadataEditor.GetMetadata(Handle, attr, out val)
96                     .ThrowIfError("Failed to get metadata");
97
98                 return Marshal.PtrToStringAnsi(val);
99             }
100             finally
101             {
102                 Interop.Libc.Free(val);
103             }
104         }
105
106         private void SetParam(MetadataEditorAttr attr, string value)
107         {
108             if (_isFileReadOnly)
109             {
110                 throw new InvalidOperationException("The media file is read-only.");
111             }
112
113             Interop.MetadataEditor.SetMetadata(Handle, attr, value).ThrowIfError("Failed to set value");
114         }
115
116         /// <summary>
117         /// Gets or sets the artist of media.
118         /// </summary>
119         /// <since_tizen> 3 </since_tizen>
120         /// <exception cref="InvalidOperationException">The file is read-only.</exception>
121         /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
122         public string Artist
123         {
124             get
125             {
126                 return GetParam(MetadataEditorAttr.Artist);
127             }
128
129             set
130             {
131                 SetParam(MetadataEditorAttr.Artist, value);
132             }
133         }
134
135         /// <summary>
136         /// Gets or sets the title of media.
137         /// </summary>
138         /// <since_tizen> 3 </since_tizen>
139         /// <exception cref="InvalidOperationException">The file is read-only.</exception>
140         /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
141         public string Title
142         {
143             get
144             {
145                 return GetParam(MetadataEditorAttr.Title);
146             }
147
148             set
149             {
150                 SetParam(MetadataEditorAttr.Title, value);
151             }
152         }
153
154         /// <summary>
155         /// Gets or sets the album name of media.
156         /// </summary>
157         /// <since_tizen> 3 </since_tizen>
158         /// <exception cref="InvalidOperationException">The file is read-only.</exception>
159         /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
160         public string Album
161         {
162             get
163             {
164                 return GetParam(MetadataEditorAttr.Album);
165             }
166
167             set
168             {
169                 SetParam(MetadataEditorAttr.Album, value);
170             }
171         }
172
173         /// <summary>
174         /// Gets or sets the genre of media.
175         /// </summary>
176         /// <since_tizen> 3 </since_tizen>
177         /// <exception cref="InvalidOperationException">The file is read-only.</exception>
178         /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
179         public string Genre
180         {
181             get
182             {
183                 return GetParam(MetadataEditorAttr.Genre);
184             }
185
186             set
187             {
188                 SetParam(MetadataEditorAttr.Genre, value);
189             }
190         }
191
192         /// <summary>
193         /// Gets or sets the author of media.
194         /// </summary>
195         /// <since_tizen> 3 </since_tizen>
196         /// <exception cref="InvalidOperationException">The file is read-only.</exception>
197         /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
198         public string Author
199         {
200             get
201             {
202                 return GetParam(MetadataEditorAttr.Author);
203             }
204
205             set
206             {
207                 SetParam(MetadataEditorAttr.Author, value);
208             }
209         }
210
211         /// <summary>
212         /// Gets or sets the copyright of media.
213         /// </summary>
214         /// <since_tizen> 3 </since_tizen>
215         /// <exception cref="InvalidOperationException">The file is read-only.</exception>
216         /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
217         public string Copyright
218         {
219             get
220             {
221                 return GetParam(MetadataEditorAttr.Copyright);
222             }
223
224             set
225             {
226                 SetParam(MetadataEditorAttr.Copyright, value);
227             }
228         }
229
230         /// <summary>
231         /// Gets or sets the date of media.
232         /// </summary>
233         /// <since_tizen> 3 </since_tizen>
234         /// <remarks>
235         /// If the media contains the ID3 tag, this refers to the recorded date.
236         /// If the media is a mp4 format, this refers to the year, and the value to set will be converted into integer.
237         /// </remarks>
238         /// <exception cref="InvalidOperationException">The file is read-only.</exception>
239         /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
240         public string Date
241         {
242             get
243             {
244                 return GetParam(MetadataEditorAttr.Date);
245             }
246
247             set
248             {
249                 SetParam(MetadataEditorAttr.Date, value);
250             }
251         }
252
253         /// <summary>
254         /// Gets or sets the description of media.
255         /// </summary>
256         /// <since_tizen> 3 </since_tizen>
257         /// <exception cref="InvalidOperationException">The file is read-only.</exception>
258         /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
259         public string Description
260         {
261             get
262             {
263                 return GetParam(MetadataEditorAttr.Description);
264             }
265
266             set
267             {
268                 SetParam(MetadataEditorAttr.Description, value);
269             }
270         }
271
272         /// <summary>
273         /// Gets or sets the comment of media.
274         /// </summary>
275         /// <since_tizen> 3 </since_tizen>
276         /// <exception cref="InvalidOperationException">The file is read-only.</exception>
277         /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
278         public string Comment
279         {
280             get
281             {
282                 return GetParam(MetadataEditorAttr.Comment);
283             }
284
285             set
286             {
287                 SetParam(MetadataEditorAttr.Comment, value);
288             }
289         }
290
291         /// <summary>
292         /// Gets or sets the track number of media.
293         /// </summary>
294         /// <since_tizen> 3 </since_tizen>
295         /// <exception cref="InvalidOperationException">The file is read-only.</exception>
296         /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
297         public string TrackNumber
298         {
299             get
300             {
301                 return GetParam(MetadataEditorAttr.TrackNumber);
302             }
303
304             set
305             {
306                 SetParam(MetadataEditorAttr.TrackNumber, value);
307             }
308         }
309
310         /// <summary>
311         /// Gets the count of album arts of media.
312         /// </summary>
313         /// <since_tizen> 3 </since_tizen>
314         /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
315         public int PictureCount
316         {
317             get => int.TryParse(GetParam(MetadataEditorAttr.PictureCount), out var value) ? value : 0;
318         }
319
320         /// <summary>
321         /// Gets or sets the conductor of media.
322         /// </summary>
323         /// <since_tizen> 3 </since_tizen>
324         /// <exception cref="InvalidOperationException">The file is read-only.</exception>
325         /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
326         public string Conductor
327         {
328             get
329             {
330                 return GetParam(MetadataEditorAttr.Conductor);
331             }
332
333             set
334             {
335                 SetParam(MetadataEditorAttr.Conductor, value);
336             }
337         }
338
339         /// <summary>
340         /// Gets or sets the unsynchronized lyrics of media.
341         /// </summary>
342         /// <since_tizen> 3 </since_tizen>
343         /// <exception cref="InvalidOperationException">The file is read-only.</exception>
344         /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
345         public string UnsyncLyrics
346         {
347             get
348             {
349                 return GetParam(MetadataEditorAttr.UnsyncLyrics);
350             }
351
352             set
353             {
354                 SetParam(MetadataEditorAttr.UnsyncLyrics, value);
355             }
356         }
357
358         /// <summary>
359         /// Writes the modified metadata to the media file.
360         /// </summary>
361         /// <exception cref="InvalidOperationException">
362         ///     An internal error occurs.<br/>
363         ///     -or-<br/>
364         ///     The file is read-only.
365         /// </exception>
366         /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
367         /// <since_tizen> 3 </since_tizen>
368         public void Commit()
369         {
370             if (_isFileReadOnly)
371             {
372                 throw new InvalidOperationException("The media file is read-only.");
373             }
374
375             Interop.MetadataEditor.UpdateMetadata(Handle).ThrowIfError("Failed to update file");
376         }
377
378         /// <summary>
379         /// Gets the artwork image in the media file.
380         /// </summary>
381         /// <since_tizen> 3 </since_tizen>
382         /// <param name="index">The index of the picture to import.</param>
383         /// <returns>The artwork included in the media file.</returns>
384         /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
385         /// <exception cref="ArgumentOutOfRangeException">
386         ///     <paramref name="index"/> is less than zero.<br/>
387         ///     -or-<br/>
388         ///     <paramref name="index"/> is greater than or equal to <see cref="PictureCount"/>.
389         /// </exception>
390         /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
391         public Artwork GetPicture(int index)
392         {
393             if (index < 0)
394             {
395                 throw new ArgumentOutOfRangeException(nameof(index), index,
396                     "Index should not be less than zero.");
397             }
398
399             if (index >= PictureCount)
400             {
401                 throw new ArgumentOutOfRangeException(nameof(index), index,
402                     "Index should not be greater thor or equal to PictureCount.");
403             }
404
405             IntPtr data = IntPtr.Zero;
406             IntPtr mimeType = IntPtr.Zero;
407
408             try
409             {
410                 Interop.MetadataEditor.GetPicture(Handle, index, out data, out var size, out mimeType).
411                     ThrowIfError("Failed to get the value");
412
413                 if (size > 0)
414                 {
415                     byte[] tmpBuf = new byte[size];
416                     Marshal.Copy(data, tmpBuf, 0, size);
417
418                     return new Artwork(tmpBuf, Marshal.PtrToStringAnsi(mimeType));
419                 }
420
421                 return null;
422             }
423             finally
424             {
425                 if (data != IntPtr.Zero)
426                 {
427                     Interop.Libc.Free(data);
428                 }
429
430                 if (mimeType != IntPtr.Zero)
431                 {
432                     Interop.Libc.Free(mimeType);
433                 }
434             }
435         }
436
437         /// <summary>
438         /// Appends the picture to the media file.
439         /// </summary>
440         /// <since_tizen> 3 </since_tizen>
441         /// <param name="path">The path of the picture for adding to the metadata.</param>
442         /// <exception cref="InvalidOperationException">
443         ///     An internal error occurs.<br/>
444         ///     -or-<br/>
445         ///     The media file is read-only.
446         /// </exception>
447         /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
448         /// <exception cref="FileNotFoundException">The file does not exist.</exception>
449         /// <exception cref="UnauthorizedAccessException">The caller does not have required privilege to access the file.</exception>
450         /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
451         /// <exception cref="FileFormatException">The specified file is not supported.</exception>
452         public void AddPicture(string path)
453         {
454             if (path == null)
455             {
456                 throw new ArgumentNullException(nameof(path));
457             }
458
459             if (File.Exists(path) == false)
460             {
461                 throw new FileNotFoundException("File does not exist.", path);
462             }
463
464             if (_isFileReadOnly)
465             {
466                 throw new InvalidOperationException("The media file is read-only.");
467             }
468
469             Interop.MetadataEditor.AddPicture(Handle, path).
470                 ThrowIfError("Failed to append picture");
471         }
472
473         /// <summary>
474         /// Removes the picture from the media file.
475         /// </summary>
476         /// <since_tizen> 3 </since_tizen>
477         /// <param name="index">The index of the picture to remove.</param>
478         /// <exception cref="InvalidOperationException">
479         ///     An internal error occurs.<br/>
480         ///     -or-<br/>
481         ///     The media file is read-only.
482         /// </exception>
483         /// <exception cref="ArgumentOutOfRangeException">
484         ///     <paramref name="index"/> is less than zero.<br/>
485         ///     -or-<br/>
486         ///     <paramref name="index"/> is greater than or equal to <see cref="PictureCount"/>.
487         /// </exception>
488         /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
489         public void RemovePicture(int index)
490         {
491             if (index < 0)
492             {
493                 throw new ArgumentOutOfRangeException("Index should be larger than 0 [" + index + "]");
494             }
495
496             if (index >= PictureCount)
497             {
498                 throw new ArgumentOutOfRangeException(nameof(index), index,
499                     "Index should not be greater thor or equal to PictureCount.");
500             }
501
502             if (_isFileReadOnly)
503             {
504                 throw new InvalidOperationException("The media file is read-only.");
505             }
506
507             Interop.MetadataEditor.RemovePicture(Handle, index).ThrowIfError("Failed to remove picture");
508         }
509
510         /// <summary>
511         /// Finalizes an instance of the MetadataEditor class.
512         /// </summary>
513         ~MetadataEditor()
514         {
515             Dispose(false);
516         }
517
518         /// <summary>
519         /// Releases the resources used by the <see cref="MetadataEditor"/> object.
520         /// </summary>
521         /// <param name="disposing">
522         /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
523         /// </param>
524         /// <since_tizen> 3 </since_tizen>
525         protected virtual void Dispose(bool disposing)
526         {
527             if (!_disposed)
528             {
529                 if (_handle != IntPtr.Zero)
530                 {
531                     Interop.MetadataEditor.Destroy(_handle);
532                     _handle = IntPtr.Zero;
533                 }
534
535                 _disposed = true;
536             }
537         }
538
539         /// <summary>
540         /// Releases all resources used by the <see cref="MetadataEditor"/> object.
541         /// </summary>
542         /// <since_tizen> 3 </since_tizen>
543         public void Dispose()
544         {
545             Dispose(true);
546             GC.SuppressFinalize(this);
547         }
548     }
549 }