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 MP3 and MP4 files.
25 /// Since 6.0, WAV, FLAC, OGG files are supported as well.
28 /// If you want to access only an internal storage,
29 /// you should add privilege http://tizen.org/privilege/mediastorage.<br/>
30 /// Or if you want to access only an external storage,
31 /// you should add privilege http://tizen.org/privilege/externalstorage.
33 /// <since_tizen> 3 </since_tizen>
34 public class MetadataEditor : IDisposable
36 private bool _disposed = false;
37 private IntPtr _handle = IntPtr.Zero;
38 private bool _isFileReadOnly;
44 if (_handle == IntPtr.Zero)
46 throw new ObjectDisposedException(nameof(MetadataEditor));
54 /// Initializes a new instance of the <see cref="MetadataEditor"/> class with the specified path.
56 /// <since_tizen> 3 </since_tizen>
57 /// <param name="path">The path of the media file to edit the metadata.</param>
58 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
59 /// <exception cref="ArgumentException"><paramref name="path"/> is a zero-length string, contains only white space.</exception>
60 /// <exception cref="FileFormatException">The file is not supported.</exception>
61 /// <exception cref="FileNotFoundException">The file does not exist.</exception>
62 /// <exception cref="UnauthorizedAccessException">The caller does not have required privilege to access the file.</exception>
63 public MetadataEditor(string path)
67 throw new ArgumentNullException(nameof(path));
70 if (string.IsNullOrWhiteSpace(path))
72 throw new ArgumentException($"{nameof(path)} is a zero-length string.", nameof(path));
75 Interop.MetadataEditor.Create(out _handle).ThrowIfError("Failed to create metadata");
79 Interop.MetadataEditor.SetPath(Handle, path).ThrowIfError("Failed to set path");
81 _isFileReadOnly = File.GetAttributes(path).HasFlag(FileAttributes.ReadOnly);
85 Interop.MetadataEditor.Destroy(_handle);
90 private string GetParam(MetadataEditorAttr attr)
92 IntPtr val = IntPtr.Zero;
96 Interop.MetadataEditor.GetMetadata(Handle, attr, out val)
97 .ThrowIfError("Failed to get metadata");
99 return Marshal.PtrToStringAnsi(val);
103 Interop.Libc.Free(val);
107 private void SetParam(MetadataEditorAttr attr, string value)
111 throw new InvalidOperationException("The media file is read-only.");
114 Interop.MetadataEditor.SetMetadata(Handle, attr, value).ThrowIfError("Failed to set value");
118 /// Gets or sets the artist of media.
120 /// <since_tizen> 3 </since_tizen>
121 /// <exception cref="InvalidOperationException">
122 /// The file is read-only.<br/>
124 /// The malformed file which cannot be updatable.<br/>
128 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
133 return GetParam(MetadataEditorAttr.Artist);
138 SetParam(MetadataEditorAttr.Artist, value);
143 /// Gets or sets the title of media.
145 /// <since_tizen> 3 </since_tizen>
146 /// <exception cref="InvalidOperationException">
147 /// The file is read-only.<br/>
149 /// The malformed file which cannot be updatable.<br/>
153 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
158 return GetParam(MetadataEditorAttr.Title);
163 SetParam(MetadataEditorAttr.Title, value);
168 /// Gets or sets the album name of media.
170 /// <since_tizen> 3 </since_tizen>
171 /// <exception cref="InvalidOperationException">
172 /// The file is read-only.<br/>
174 /// The malformed file which cannot be updatable.<br/>
178 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
183 return GetParam(MetadataEditorAttr.Album);
188 SetParam(MetadataEditorAttr.Album, value);
193 /// Gets or sets the genre of media.
195 /// <since_tizen> 3 </since_tizen>
196 /// <exception cref="InvalidOperationException">
197 /// The file is read-only.<br/>
199 /// The malformed file which cannot be updatable.<br/>
203 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
208 return GetParam(MetadataEditorAttr.Genre);
213 SetParam(MetadataEditorAttr.Genre, value);
218 /// Gets or sets the author of media.
220 /// <since_tizen> 3 </since_tizen>
221 /// <exception cref="InvalidOperationException">
222 /// The file is read-only.<br/>
224 /// The malformed file which cannot be updatable.<br/>
228 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
233 return GetParam(MetadataEditorAttr.Author);
238 SetParam(MetadataEditorAttr.Author, value);
243 /// Gets or sets the copyright of media.
245 /// <since_tizen> 3 </since_tizen>
246 /// <exception cref="InvalidOperationException">
247 /// The file is read-only.<br/>
249 /// The malformed file which cannot be updatable.<br/>
253 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
254 public string Copyright
258 return GetParam(MetadataEditorAttr.Copyright);
263 SetParam(MetadataEditorAttr.Copyright, value);
268 /// Gets or sets the date of media.
270 /// <since_tizen> 3 </since_tizen>
272 /// If the media contains the ID3 tag, this refers to the recorded date.
273 /// If the media is a mp4 format, this refers to the year, and the value to set will be converted into integer.
275 /// <exception cref="InvalidOperationException">
276 /// The file is read-only.<br/>
278 /// The malformed file which cannot be updatable.<br/>
282 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
287 return GetParam(MetadataEditorAttr.Date);
292 SetParam(MetadataEditorAttr.Date, value);
297 /// Gets or sets the description of media.
299 /// <since_tizen> 3 </since_tizen>
300 /// <exception cref="InvalidOperationException">
301 /// The file is read-only.<br/>
303 /// The malformed file which cannot be updatable.<br/>
307 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
308 public string Description
312 return GetParam(MetadataEditorAttr.Description);
317 SetParam(MetadataEditorAttr.Description, value);
322 /// Gets or sets the comment of media.
324 /// <since_tizen> 3 </since_tizen>
325 /// <exception cref="InvalidOperationException">
326 /// The file is read-only.<br/>
328 /// The malformed file which cannot be updatable.<br/>
332 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
333 public string Comment
337 return GetParam(MetadataEditorAttr.Comment);
342 SetParam(MetadataEditorAttr.Comment, value);
347 /// Gets or sets the track number of media.
349 /// <since_tizen> 3 </since_tizen>
350 /// <exception cref="InvalidOperationException">
351 /// The file is read-only.<br/>
353 /// The malformed file which cannot be updatable.<br/>
357 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
358 public string TrackNumber
362 return GetParam(MetadataEditorAttr.TrackNumber);
367 SetParam(MetadataEditorAttr.TrackNumber, value);
372 /// Gets the count of album arts of media.
374 /// <since_tizen> 3 </since_tizen>
375 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
376 public int PictureCount
378 get => int.TryParse(GetParam(MetadataEditorAttr.PictureCount), out var value) ? value : 0;
382 /// Gets or sets the conductor of media.
384 /// <since_tizen> 3 </since_tizen>
385 /// <exception cref="InvalidOperationException">
386 /// The file is read-only.<br/>
388 /// The malformed file which cannot be updatable.<br/>
392 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
393 public string Conductor
397 return GetParam(MetadataEditorAttr.Conductor);
402 SetParam(MetadataEditorAttr.Conductor, value);
407 /// Gets or sets the unsynchronized lyrics of media.
409 /// <since_tizen> 3 </since_tizen>
410 /// <exception cref="InvalidOperationException">
411 /// The file is read-only.<br/>
413 /// The malformed file which cannot be updatable.<br/>
417 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
418 public string UnsyncLyrics
422 return GetParam(MetadataEditorAttr.UnsyncLyrics);
427 SetParam(MetadataEditorAttr.UnsyncLyrics, value);
432 /// Writes the modified metadata to the media file.
434 /// <exception cref="InvalidOperationException">
435 /// The file is read-only.<br/>
437 /// Internal error.<br/>
439 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
440 /// <since_tizen> 3 </since_tizen>
445 throw new InvalidOperationException("The media file is read-only.");
448 Interop.MetadataEditor.UpdateMetadata(Handle).ThrowIfError("Failed to update file");
452 /// Gets the artwork image in the media file.
454 /// <since_tizen> 3 </since_tizen>
455 /// <param name="index">The index of the picture to import.</param>
456 /// <returns>The artwork included in the media file.</returns>
457 /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
458 /// <exception cref="ArgumentOutOfRangeException">
459 /// <paramref name="index"/> is less than zero.<br/>
461 /// <paramref name="index"/> is greater than or equal to <see cref="PictureCount"/>.
463 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
464 public Artwork GetPicture(int index)
468 throw new ArgumentOutOfRangeException(nameof(index), index,
469 "Index should not be less than zero.");
472 if (index >= PictureCount)
474 throw new ArgumentOutOfRangeException(nameof(index), index,
475 "Index should not be greater thor or equal to PictureCount.");
478 IntPtr data = IntPtr.Zero;
479 IntPtr mimeType = IntPtr.Zero;
483 Interop.MetadataEditor.GetPicture(Handle, index, out data, out var size, out mimeType).
484 ThrowIfError("Failed to get the value");
488 byte[] tmpBuf = new byte[size];
489 Marshal.Copy(data, tmpBuf, 0, size);
491 return new Artwork(tmpBuf, Marshal.PtrToStringAnsi(mimeType));
498 if (data != IntPtr.Zero)
500 Interop.Libc.Free(data);
503 if (mimeType != IntPtr.Zero)
505 Interop.Libc.Free(mimeType);
511 /// Appends the picture to the media file.
513 /// <since_tizen> 3 </since_tizen>
514 /// <param name="path">The path of the picture for adding to the metadata.</param>
515 /// <exception cref="InvalidOperationException">
516 /// The file is read-only.<br/>
518 /// The malformed file which cannot be updatable.<br/>
522 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
523 /// <exception cref="FileNotFoundException">The file does not exist.</exception>
524 /// <exception cref="UnauthorizedAccessException">The caller does not have required privilege to access the file.</exception>
525 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
526 /// <exception cref="FileFormatException">The specified file is not supported.</exception>
527 public void AddPicture(string path)
531 throw new ArgumentNullException(nameof(path));
534 if (File.Exists(path) == false)
536 throw new FileNotFoundException("File does not exist.", path);
541 throw new InvalidOperationException("The media file is read-only.");
544 Interop.MetadataEditor.AddPicture(Handle, path).
545 ThrowIfError("Failed to append picture");
549 /// Removes the picture from the media file.
551 /// <since_tizen> 3 </since_tizen>
552 /// <param name="index">The index of the picture to remove.</param>
553 /// <exception cref="InvalidOperationException">
554 /// An internal error occurs.<br/>
556 /// The media file is read-only.
558 /// <exception cref="ArgumentOutOfRangeException">
559 /// <paramref name="index"/> is less than zero.<br/>
561 /// <paramref name="index"/> is greater than or equal to <see cref="PictureCount"/>.
563 /// <exception cref="ObjectDisposedException">The <see cref="MetadataEditor"/> has already been disposed of.</exception>
564 public void RemovePicture(int index)
568 throw new ArgumentOutOfRangeException("Index should be larger than 0 [" + index + "]");
571 if (index >= PictureCount)
573 throw new ArgumentOutOfRangeException(nameof(index), index,
574 "Index should not be greater thor or equal to PictureCount.");
579 throw new InvalidOperationException("The media file is read-only.");
582 Interop.MetadataEditor.RemovePicture(Handle, index).ThrowIfError("Failed to remove picture");
586 /// Finalizes an instance of the MetadataEditor class.
594 /// Releases the resources used by the <see cref="MetadataEditor"/> object.
596 /// <param name="disposing">
597 /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
599 /// <since_tizen> 3 </since_tizen>
600 protected virtual void Dispose(bool disposing)
604 if (_handle != IntPtr.Zero)
606 Interop.MetadataEditor.Destroy(_handle);
607 _handle = IntPtr.Zero;
615 /// Releases all resources used by the <see cref="MetadataEditor"/> object.
617 /// <since_tizen> 3 </since_tizen>
618 public void Dispose()
621 GC.SuppressFinalize(this);