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.
19 using System.Runtime.InteropServices;
21 namespace Tizen.Multimedia
24 /// Provides a means to edit the metadata of the media file.
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.
32 /// <since_tizen> 3 </since_tizen>
33 public class MetadataEditor : IDisposable
35 private bool _disposed = false;
36 private IntPtr _handle = IntPtr.Zero;
37 private bool _isFileReadOnly;
43 if (_handle == IntPtr.Zero)
45 throw new ObjectDisposedException(nameof(MetadataEditor));
53 /// Initializes a new instance of the <see cref="MetadataEditor"/> class with the specified path.
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)
66 throw new ArgumentNullException(nameof(path));
69 if (string.IsNullOrWhiteSpace(path))
71 throw new ArgumentException($"{nameof(path)} is a zero-length string.", nameof(path));
74 Interop.MetadataEditor.Create(out _handle).ThrowIfError("Failed to create metadata");
78 Interop.MetadataEditor.SetPath(Handle, path).ThrowIfError("Failed to set path");
80 _isFileReadOnly = File.GetAttributes(path).HasFlag(FileAttributes.ReadOnly);
84 Interop.MetadataEditor.Destroy(_handle);
89 private string GetParam(MetadataEditorAttr attr)
91 IntPtr val = IntPtr.Zero;
95 Interop.MetadataEditor.GetMetadata(Handle, attr, out val)
96 .ThrowIfError("Failed to get metadata");
98 return Marshal.PtrToStringAnsi(val);
102 Interop.Libc.Free(val);
106 private void SetParam(MetadataEditorAttr attr, string value)
110 throw new InvalidOperationException("The media file is read-only.");
113 Interop.MetadataEditor.SetMetadata(Handle, attr, value).ThrowIfError("Failed to set value");
117 /// Gets or sets the artist of media.
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>
126 return GetParam(MetadataEditorAttr.Artist);
131 SetParam(MetadataEditorAttr.Artist, value);
136 /// Gets or sets the title of media.
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>
145 return GetParam(MetadataEditorAttr.Title);
150 SetParam(MetadataEditorAttr.Title, value);
155 /// Gets or sets the album name of media.
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>
164 return GetParam(MetadataEditorAttr.Album);
169 SetParam(MetadataEditorAttr.Album, value);
174 /// Gets or sets the genre of media.
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>
183 return GetParam(MetadataEditorAttr.Genre);
188 SetParam(MetadataEditorAttr.Genre, value);
193 /// Gets or sets the author of media.
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>
202 return GetParam(MetadataEditorAttr.Author);
207 SetParam(MetadataEditorAttr.Author, value);
212 /// Gets or sets the copyright of media.
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
221 return GetParam(MetadataEditorAttr.Copyright);
226 SetParam(MetadataEditorAttr.Copyright, value);
231 /// Gets or sets the date of media.
233 /// <since_tizen> 3 </since_tizen>
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.
238 /// <exception cref="InvalidOperationException">The file is read-only.</exception>
239 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
244 return GetParam(MetadataEditorAttr.Date);
249 SetParam(MetadataEditorAttr.Date, value);
254 /// Gets or sets the description of media.
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
263 return GetParam(MetadataEditorAttr.Description);
268 SetParam(MetadataEditorAttr.Description, value);
273 /// Gets or sets the comment of media.
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
282 return GetParam(MetadataEditorAttr.Comment);
287 SetParam(MetadataEditorAttr.Comment, value);
292 /// Gets or sets the track number of media.
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
301 return GetParam(MetadataEditorAttr.TrackNumber);
306 SetParam(MetadataEditorAttr.TrackNumber, value);
311 /// Gets the count of album arts of media.
313 /// <since_tizen> 3 </since_tizen>
314 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
315 public int PictureCount
317 get => int.TryParse(GetParam(MetadataEditorAttr.PictureCount), out var value) ? value : 0;
321 /// Gets or sets the conductor of media.
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
330 return GetParam(MetadataEditorAttr.Conductor);
335 SetParam(MetadataEditorAttr.Conductor, value);
340 /// Gets or sets the unsynchronized lyrics of media.
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
349 return GetParam(MetadataEditorAttr.UnsyncLyrics);
354 SetParam(MetadataEditorAttr.UnsyncLyrics, value);
359 /// Writes the modified metadata to the media file.
361 /// <exception cref="InvalidOperationException">
362 /// An internal error occurs.<br/>
364 /// The file is read-only.
366 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
367 /// <since_tizen> 3 </since_tizen>
372 throw new InvalidOperationException("The media file is read-only.");
375 Interop.MetadataEditor.UpdateMetadata(Handle).ThrowIfError("Failed to update file");
379 /// Gets the artwork image in the media file.
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/>
388 /// <paramref name="index"/> is greater than or equal to <see cref="PictureCount"/>.
390 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
391 public Artwork GetPicture(int index)
395 throw new ArgumentOutOfRangeException(nameof(index), index,
396 "Index should not be less than zero.");
399 if (index >= PictureCount)
401 throw new ArgumentOutOfRangeException(nameof(index), index,
402 "Index should not be greater thor or equal to PictureCount.");
405 IntPtr data = IntPtr.Zero;
406 IntPtr mimeType = IntPtr.Zero;
410 Interop.MetadataEditor.GetPicture(Handle, index, out data, out var size, out mimeType).
411 ThrowIfError("Failed to get the value");
415 byte[] tmpBuf = new byte[size];
416 Marshal.Copy(data, tmpBuf, 0, size);
418 return new Artwork(tmpBuf, Marshal.PtrToStringAnsi(mimeType));
425 if (data != IntPtr.Zero)
427 Interop.Libc.Free(data);
430 if (mimeType != IntPtr.Zero)
432 Interop.Libc.Free(mimeType);
438 /// Appends the picture to the media file.
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/>
445 /// The media file is read-only.
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)
456 throw new ArgumentNullException(nameof(path));
459 if (File.Exists(path) == false)
461 throw new FileNotFoundException("File does not exist.", path);
466 throw new InvalidOperationException("The media file is read-only.");
469 Interop.MetadataEditor.AddPicture(Handle, path).
470 ThrowIfError("Failed to append picture");
474 /// Removes the picture from the media file.
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/>
481 /// The media file is read-only.
483 /// <exception cref="ArgumentOutOfRangeException">
484 /// <paramref name="index"/> is less than zero.<br/>
486 /// <paramref name="index"/> is greater than or equal to <see cref="PictureCount"/>.
488 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
489 public void RemovePicture(int index)
493 throw new ArgumentOutOfRangeException("Index should be larger than 0 [" + index + "]");
496 if (index >= PictureCount)
498 throw new ArgumentOutOfRangeException(nameof(index), index,
499 "Index should not be greater thor or equal to PictureCount.");
504 throw new InvalidOperationException("The media file is read-only.");
507 Interop.MetadataEditor.RemovePicture(Handle, index).ThrowIfError("Failed to remove picture");
511 /// Finalizes an instance of the MetadataEditor class.
519 /// Releases the resources used by the <see cref="MetadataEditor"/> object.
521 /// <param name="disposing">
522 /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
524 /// <since_tizen> 3 </since_tizen>
525 protected virtual void Dispose(bool disposing)
529 if (_handle != IntPtr.Zero)
531 Interop.MetadataEditor.Destroy(_handle);
532 _handle = IntPtr.Zero;
540 /// Releases all resources used by the <see cref="MetadataEditor"/> object.
542 /// <since_tizen> 3 </since_tizen>
543 public void Dispose()
546 GC.SuppressFinalize(this);