Initial commit
This commit is contained in:
commit
262601edb8
2025 files changed
+218754
No files matched your search
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// StringHandler.cs:
|
||||
//
|
||||
// Author:
|
||||
// Brian Nickel (brian.nickel@gmail.com)
|
||||
//
|
||||
// Original Source:
|
||||
// id3v1tag.cpp from TagLib
|
||||
//
|
||||
// Copyright (C) 2005-2007 Brian Nickel
|
||||
// Copyright (C) 2002,2003 Scott Wheeler (Original Implementation)
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License version
|
||||
// 2.1 as published by the Free Software Foundation.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
// USA
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace TagLib.Id3v1
|
||||
{
|
||||
/// <summary>
|
||||
/// This class provides a mechanism for customizing how Id3v1 text
|
||||
/// is read and written.
|
||||
/// </summary>
|
||||
public class StringHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts raw ID3v1 text data to a <see cref="string" />
|
||||
/// object.
|
||||
/// </summary>
|
||||
/// <param name="data">
|
||||
/// A <see cref="ByteVector" /> object containing raw Id3v1
|
||||
/// text data.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A <see cref="string"/> object containing the converted
|
||||
/// text.
|
||||
/// </returns>
|
||||
public virtual string Parse (ByteVector data)
|
||||
{
|
||||
if (data == null)
|
||||
throw new ArgumentNullException (nameof (data));
|
||||
|
||||
string output = data.ToString (StringType.Latin1).Trim ();
|
||||
int i = output.IndexOf ('\0');
|
||||
return (i >= 0) ? output.Substring (0, i) : output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="string" /> object to raw ID3v1 text
|
||||
/// data.
|
||||
/// </summary>
|
||||
/// <param name="text">
|
||||
/// A <see cref="string" /> object to convert.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A <see cref="ByteVector"/> containing the raw ID3v1 text
|
||||
/// data.
|
||||
/// </returns>
|
||||
public virtual ByteVector Render (string text)
|
||||
{
|
||||
return ByteVector.FromString (text, StringType.Latin1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 720a5c9b93ce83d098d9de6865c6fe17
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,465 @@
|
||||
//
|
||||
// Tag.cs: Provide support for reading and writing ID3v1 tags.
|
||||
//
|
||||
// Author:
|
||||
// Brian Nickel (brian.nickel@gmail.com)
|
||||
//
|
||||
// Original Source:
|
||||
// id3v1tag.cpp from TagLib
|
||||
//
|
||||
// Copyright (C) 2005-2007 Brian Nickel
|
||||
// Copyright (C) 2002,2003 Scott Wheeler (Original Implementation)
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License version
|
||||
// 2.1 as published by the Free Software Foundation.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
// USA
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace TagLib.Id3v1
|
||||
{
|
||||
/// <summary>
|
||||
/// This class extends <see cref="Tag" /> to provide support for
|
||||
/// reading and writing tags stored in the ID3v1.1 format.
|
||||
/// </summary>
|
||||
public class Tag : TagLib.Tag
|
||||
{
|
||||
|
||||
#region Private Fields
|
||||
|
||||
/// <summary>
|
||||
/// Contains the title.
|
||||
/// </summary>
|
||||
string title;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the semicolon separated performers.
|
||||
/// </summary>
|
||||
string artist;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the album name.
|
||||
/// </summary>
|
||||
string album;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the 4 digit year.
|
||||
/// </summary>
|
||||
string year;
|
||||
|
||||
/// <summary>
|
||||
/// Contains a comment on track.
|
||||
/// </summary>
|
||||
string comment;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the track number in the album.
|
||||
/// </summary>
|
||||
byte track;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the genre index.
|
||||
/// </summary>
|
||||
byte genre;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
#region Public Static Fields
|
||||
|
||||
/// <summary>
|
||||
/// The size of a ID3v1 tag.
|
||||
/// </summary>
|
||||
public const uint Size = 128;
|
||||
|
||||
/// <summary>
|
||||
/// The identifier used to recognize a ID3v1 tags.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// "TAG"
|
||||
/// </value>
|
||||
public static readonly ReadOnlyByteVector FileIdentifier = "TAG";
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Constructs and initializes a new instance of <see
|
||||
/// cref="Tag" /> with no contents.
|
||||
/// </summary>
|
||||
public Tag ()
|
||||
{
|
||||
Clear ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs and initializes a new instance of <see
|
||||
/// cref="Tag" /> by reading the contents from a specified
|
||||
/// position in a specified file.
|
||||
/// </summary>
|
||||
/// <param name="file">
|
||||
/// A <see cref="File" /> object containing the file from
|
||||
/// which the contents of the new instance is to be read.
|
||||
/// </param>
|
||||
/// <param name="position">
|
||||
/// A <see cref="long" /> value specify at what position to
|
||||
/// read the tag.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="file" /> is <see langword="null" />.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="position" /> is less than zero or greater
|
||||
/// than the size of the file.
|
||||
/// </exception>
|
||||
/// <exception cref="CorruptFileException">
|
||||
/// The file does not contain <see cref="FileIdentifier" />
|
||||
/// at the given position.
|
||||
/// </exception>
|
||||
public Tag (File file, long position)
|
||||
{
|
||||
if (file == null)
|
||||
throw new ArgumentNullException (nameof (file));
|
||||
|
||||
file.Mode = File.AccessMode.Read;
|
||||
|
||||
if (position < 0 || position > file.Length - Size)
|
||||
throw new ArgumentOutOfRangeException (nameof (position));
|
||||
|
||||
file.Seek (position);
|
||||
|
||||
// read the tag -- always 128 bytes
|
||||
|
||||
ByteVector data = file.ReadBlock ((int)Size);
|
||||
|
||||
// some initial sanity checking
|
||||
|
||||
if (!data.StartsWith (FileIdentifier))
|
||||
throw new CorruptFileException ("ID3v1 data does not start with identifier.");
|
||||
|
||||
Parse (data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs and initializes a new instance of <see
|
||||
/// cref="Tag" /> by reading the contents from a specified
|
||||
/// <see cref="ByteVector" /> object.
|
||||
/// </summary>
|
||||
/// <param name="data">
|
||||
/// A <see cref="ByteVector" /> object to read the tag from.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="data" /> is <see langword="null" />.
|
||||
/// </exception>
|
||||
/// <exception cref="CorruptFileException">
|
||||
/// <paramref name="data" /> is less than 128 bytes or does
|
||||
/// not start with <see cref="FileIdentifier" />.
|
||||
/// </exception>
|
||||
public Tag (ByteVector data)
|
||||
{
|
||||
if (data == null)
|
||||
throw new ArgumentNullException (nameof (data));
|
||||
|
||||
if (data.Count < Size)
|
||||
throw new CorruptFileException ("ID3v1 data is less than 128 bytes long.");
|
||||
|
||||
if (!data.StartsWith (FileIdentifier))
|
||||
throw new CorruptFileException ("ID3v1 data does not start with identifier.");
|
||||
|
||||
Parse (data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Renders the current instance as a raw ID3v1 tag.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="ByteVector" /> object containing the
|
||||
/// rendered tag.
|
||||
/// </returns>
|
||||
public ByteVector Render ()
|
||||
{
|
||||
var data = new ByteVector {
|
||||
FileIdentifier,
|
||||
DefaultStringHandler.Render (title).Resize (30),
|
||||
DefaultStringHandler.Render (artist).Resize (30),
|
||||
DefaultStringHandler.Render (album).Resize (30),
|
||||
DefaultStringHandler.Render (year).Resize (4),
|
||||
DefaultStringHandler.Render (comment).Resize (28),
|
||||
0,
|
||||
track,
|
||||
genre
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region Public Static Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets and sets the <see cref="StringHandler" /> object
|
||||
/// to use when reading and writing ID3v1 fields.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="StringHandler" /> object to use when
|
||||
/// processing fields.
|
||||
/// </value>
|
||||
public static StringHandler DefaultStringHandler { get; set; } = new StringHandler ();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Populates the current instance by parsing the contents of
|
||||
/// a raw ID3v1 tag.
|
||||
/// </summary>
|
||||
/// <param name="data">
|
||||
/// A <see cref="ByteVector" /> object containing the
|
||||
/// starting with an ID3v1 tag.
|
||||
/// </param>
|
||||
void Parse (ByteVector data)
|
||||
{
|
||||
title = DefaultStringHandler.Parse (data.Mid (3, 30));
|
||||
artist = DefaultStringHandler.Parse (data.Mid (33, 30));
|
||||
album = DefaultStringHandler.Parse (data.Mid (63, 30));
|
||||
year = DefaultStringHandler.Parse (data.Mid (93, 4));
|
||||
|
||||
// Check for ID3v1.1 -- Note that ID3v1 *does not*
|
||||
// support "track zero" -- this is not a bug in TagLib.
|
||||
// Since a zeroed byte is what we would expect to
|
||||
// indicate the end of a C-String, specifically the
|
||||
// comment string, a value of zero must be assumed to be
|
||||
// just that.
|
||||
|
||||
if (data[125] == 0 && data[126] != 0) {
|
||||
// ID3v1.1 detected
|
||||
comment = DefaultStringHandler.Parse (data.Mid (97, 28));
|
||||
track = data[126];
|
||||
} else {
|
||||
comment = DefaultStringHandler.Parse (data.Mid (97, 30));
|
||||
}
|
||||
|
||||
genre = data[127];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region TagLib.Tag
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tag types contained in the current instance.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Always <see cref="TagTypes.Id3v1" />.
|
||||
/// </value>
|
||||
public override TagTypes TagTypes {
|
||||
get { return TagTypes.Id3v1; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets and sets the title for the media described by the
|
||||
/// current instance.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string" /> object containing the title for
|
||||
/// the media described by the current instance or <see
|
||||
/// langword="null" /> if no value is present.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// When stored on disk, only the first 30 bytes of the
|
||||
/// Latin-1 encoded value will be stored. This may result in
|
||||
/// lost data.
|
||||
/// </remarks>
|
||||
public override string Title {
|
||||
get {
|
||||
return string.IsNullOrEmpty (title) ?
|
||||
null : title;
|
||||
}
|
||||
set {
|
||||
title = value != null ?
|
||||
value.Trim () : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets and sets the performers or artists who performed in
|
||||
/// the media described by the current instance.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="T:string[]" /> containing the performers or
|
||||
/// artists who performed in the media described by the
|
||||
/// current instance or an empty array if no value is
|
||||
/// present.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// When stored on disk, only the first 30 bytes of the
|
||||
/// Latin-1 encoded value will be stored, minus a byte for
|
||||
/// each additionial performer (i.e. two performers will only
|
||||
/// have 29 bytes and three performers will only have 28
|
||||
/// bytes).This may result in lost data.
|
||||
/// </remarks>
|
||||
public override string[] Performers {
|
||||
get {
|
||||
return string.IsNullOrEmpty (artist) ? new string[0] : artist.Split (';');
|
||||
}
|
||||
set {
|
||||
artist = value != null ? string.Join (";", value) : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets and sets the album of the media represented by the
|
||||
/// current instance.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string" /> object containing the album of
|
||||
/// the media represented by the current instance or <see
|
||||
/// langword="null" /> if no value is present.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// When stored on disk, only the first 30 bytes of the
|
||||
/// Latin-1 encoded value will be stored. This may result in
|
||||
/// lost data.
|
||||
/// </remarks>
|
||||
public override string Album {
|
||||
get {
|
||||
return string.IsNullOrEmpty (album) ? null : album;
|
||||
}
|
||||
set {
|
||||
album = value != null ? value.Trim () : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets and sets a user comment on the media represented by
|
||||
/// the current instance.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string" /> object containing user comments
|
||||
/// on the media represented by the current instance or <see
|
||||
/// langword="null" /> if no value is present.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// When stored on disk, only the first 28 bytes of the
|
||||
/// Latin-1 encoded value will be stored. This may result in
|
||||
/// lost data.
|
||||
/// </remarks>
|
||||
public override string Comment {
|
||||
get {
|
||||
return string.IsNullOrEmpty (comment) ? null : comment;
|
||||
}
|
||||
set {
|
||||
comment = value != null ? value.Trim () : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets and sets the genres of the media represented by the
|
||||
/// current instance.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="T:string[]" /> containing the genres of the
|
||||
/// media represented by the current instance or an empty
|
||||
/// array if no value is present.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Only first genre will be stored and only if it is an
|
||||
/// exact match for a value appearing in <see
|
||||
/// cref="TagLib.Genres.Audio" />. All other values will
|
||||
/// result in the property being cleared.
|
||||
/// </remarks>
|
||||
public override string[] Genres {
|
||||
get {
|
||||
string genre_name = TagLib.Genres.IndexToAudio (genre);
|
||||
|
||||
return (genre_name != null) ? new [] { genre_name } : new string[0];
|
||||
}
|
||||
set {
|
||||
genre = (value == null || value.Length == 0) ?
|
||||
(byte)255 : TagLib.Genres.AudioToIndex (value[0].Trim ());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets and sets the year that the media represented by the
|
||||
/// current instance was recorded.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="uint" /> containing the year that the media
|
||||
/// represented by the current instance was created or zero
|
||||
/// if no value is present.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Only values between 1 and 9999 will be stored, all other
|
||||
/// values will result in the property being zeroed.
|
||||
/// </remarks>
|
||||
public override uint Year {
|
||||
get {
|
||||
return uint.TryParse (year, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value) ? value : 0;
|
||||
}
|
||||
set {
|
||||
year = (value > 0 && value < 10000) ? value.ToString (CultureInfo.InvariantCulture) : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets and sets the position of the media represented by
|
||||
/// the current instance in its containing album.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="uint" /> containing the position of the
|
||||
/// media represented by the current instance in its
|
||||
/// containing album or zero if not specified.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Only values between 1 and 255 will be stored, all other
|
||||
/// values will result in the property being zeroed.
|
||||
/// </remarks>
|
||||
public override uint Track {
|
||||
get { return track; }
|
||||
set { track = (byte)(value < 256 ? value : 0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the values stored in the current instance.
|
||||
/// </summary>
|
||||
public override void Clear ()
|
||||
{
|
||||
title = artist = album = year = comment = null;
|
||||
track = 0;
|
||||
genre = 255;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4de3fb5ee7f5c2f278a83c5c573c44fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in new issue
Block a user