Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 4 additions & 24 deletions src/id3v2/frames/attachmentFrame.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import Frame from "./frame";
import Id3v2Settings from "../id3v2Settings";
import {ByteVector, StringType} from "../../byteVector";
import {CorruptFileError} from "../../errors";
import {IFileAbstraction} from "../../fileAbstraction";
import {Frame, FrameClassType} from "./frame";
import {Id3v2FrameHeader} from "./frameHeader";
import {FrameIdentifiers} from "../frameIdentifiers";
import {IPicture, Picture, PictureLazy, PictureType} from "../../picture";
import {Guards} from "../../utils";
import {ArrayUtils, Guards} from "../../utils";

export default class AttachmentFrame extends Frame implements IPicture {
// NOTE: It probably doesn't look necessary to implement IPicture, but it makes converting a
Expand Down Expand Up @@ -118,9 +118,6 @@ export default class AttachmentFrame extends Frame implements IPicture {

// #region Properties

/** @inheritDoc */
public get frameClassType(): FrameClassType { return FrameClassType.AttachmentFrame; }

/**
* Gets the image data stored in the current instance.
*/
Expand Down Expand Up @@ -250,26 +247,9 @@ export default class AttachmentFrame extends Frame implements IPicture {
return frame;
}

/**
* Get a specified attachment frame from the specified tag, optionally creating it if it does
* not exist.
* @param frames List of attachment frames to search
* @param description Description to match
* @param type Picture type to match
* @returns Matching frame or `undefined` if a match wasn't found and `create` is `false`
*/
public static find(
frames: AttachmentFrame[],
description?: string,
type: PictureType = PictureType.Other
): AttachmentFrame {
public static filterFrames(frames: Frame[]): AttachmentFrame[] {
Guards.truthy(frames, "frames");
return frames.find((f) => {
if (description && f.description !== description) { return false; }
// noinspection RedundantIfStatementJS
if (type !== PictureType.Other && f.type !== type) { return false; }
return true;
});
return ArrayUtils.ofType(frames, AttachmentFrame);
}

/**
Expand Down
99 changes: 4 additions & 95 deletions src/id3v2/frames/commentsFrame.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import Frame from "./frame";
import Id3v2Settings from "../id3v2Settings";
import {ByteVector, StringType} from "../../byteVector";
import {CorruptFileError} from "../../errors";
import {Frame, FrameClassType} from "./frame";
import {Id3v2FrameHeader} from "./frameHeader";
import {FrameIdentifiers} from "../frameIdentifiers";
import {Guards} from "../../utils";
import {ArrayUtils, Guards} from "../../utils";

/**
* Class that extends {@link Frame}, implementing support for ID3v2 Comments (COMM) frames.
* A {@link CommentsFrame} should be used for storing user-readable comments on the media file.
* When reading comments from a file, {@link CommentsFrame.findPreferred} should be used as it
* gracefully falls back to comments that you, as a developer, may not be expecting.
*/
export default class CommentsFrame extends Frame {
private _description: string;
Expand Down Expand Up @@ -91,9 +89,6 @@ export default class CommentsFrame extends Frame {

// #region Public Properties

/** @inheritDoc */
public get frameClassType(): FrameClassType { return FrameClassType.CommentsFrame; }

/**
* Gets the description stored in the current instance, or empty string if not set.
*/
Expand Down Expand Up @@ -143,95 +138,9 @@ export default class CommentsFrame extends Frame {

// #endregion

/**
* Gets a comment frame that matched the provided parameters from the list of frames
* @param frames Frames to search for best matching frame
* @param description Description of the comments frame to match
* @param language Optional, ISO-639-2 language code to match
* @returns Object containing the matching frame or `undefined` if a match was not found
*/
public static find(frames: CommentsFrame[], description: string, language?: string): CommentsFrame {
Guards.truthy(frames, "frames");

return frames.find((f) => {
if (f.description !== description) { return false; }
// noinspection RedundantIfStatementJS
if (language && f.language !== language) { return false; }
return true;
});
}

/**
* Gets all comment frames that match the provided parameters from the list of frames
* @param frames Frames to search
* @param description Description of the comments frame to match
* @param language Optional, ISO-639-2 language code to match
* @returns
* Array of comments frames that match the provided parameters or an
* empty array if none were found
*/
public static findAll(frames: CommentsFrame[], description: string, language?: string): CommentsFrame[] {
public static filterFrames(frames: Frame[]): CommentsFrame[] {
Guards.truthy(frames, "frames");

return frames.filter((f) => {
if (f.description !== description) { return false; }
// noinspection RedundantIfStatementJS
if (language && f.language !== language) { return false; }
return true;
});
}

/**
* Gets a specified comments frame from the specified tag, trying to match the description and
* language but accepting an incomplete match.
* The method tries matching with the following order of precedence:
* * The first frame with a matching description and language
* * The first frame with a matching language
* * The first frame with a matching description
* * The first frame
* @param frames Frames to search for best matching frame
* @param description Description to match
* @param language ISO-639-2 language code to match
*/
public static findPreferred(frames: CommentsFrame[], description: string, language?: string): CommentsFrame {
Guards.truthy(frames, "frames");

// Original .NET comments:
// This is weird, so bear with me. The best thing we can have is something straightforward
// and in our own language. If it has a description, then it is probably used for something
// other than an actual comment. If that doesn't work, we'd still rather have something in
// our own language than something in another. After that, all we have left are things in
// other languages, so we'd rather have one with actual content, so we try to get one with
// no description first.

const skipITunes = !description || !description.startsWith("iTun");

let bestValue = -1;
let bestFrame: CommentsFrame;

for (const frame of frames) {
if (skipITunes && frame.description.startsWith("iTun")) {
continue;
}

const sameName = frame.description === description;
const sameLang = frame.language === language;

if (sameName && sameLang) {
return frame;
}

const value = sameLang ? 2 : sameName ? 1 : 0;

if (value <= bestValue) {
continue;
}

bestValue = value;
bestFrame = frame;
}

return bestFrame;
return ArrayUtils.ofType(frames, CommentsFrame);
}

/** @inheritDoc */
Expand Down
12 changes: 7 additions & 5 deletions src/id3v2/frames/eventTimeCodeFrame.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Frame from "./frame";
import {ByteVector} from "../../byteVector";
import {Frame, FrameClassType} from "./frame";
import {Id3v2FrameFlags, Id3v2FrameHeader} from "./frameHeader";
import {FrameIdentifiers} from "../frameIdentifiers";
import {Guards} from "../../utils";
import {ArrayUtils, Guards} from "../../utils";
import {EventType, TimestampFormat} from "../utilTypes";
import {CorruptFileError} from "../../errors";

Expand Down Expand Up @@ -166,9 +166,6 @@ export class EventTimeCodeFrame extends Frame {

// #region Properties

/** @inheritDoc */
public get frameClassType(): FrameClassType { return FrameClassType.EventTimeCodeFrame; }

/**
* Gets the event this frame contains. Each {@link EventTimeCode} represents a single event at a
* certain point in time.
Expand All @@ -192,6 +189,11 @@ export class EventTimeCodeFrame extends Frame {

// #region Methods

public static filterFrames(frames: Frame[]): EventTimeCodeFrame[] {
Guards.truthy(frames, "frames");
return ArrayUtils.ofType(frames, EventTimeCodeFrame);
}

/** @inheritDoc */
public clone(): Frame {
const frame = new EventTimeCodeFrame(this.header);
Expand Down
104 changes: 1 addition & 103 deletions src/id3v2/frames/frame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,107 +5,11 @@ import {Id3v2FrameFlags, Id3v2FrameHeader} from "./frameHeader";
import {FrameIdentifier} from "../frameIdentifiers";
import {Guards, NumberUtils} from "../../utils";

/**
* Enumeration of types of frames.
*/
// @TODO: We can probably get rid of this since instance of works quite well.
export enum FrameClassType {
/**
* Indicates the frame is an attachment frame.
*/
AttachmentFrame,

/**
* Indicates the frame is a comments frame.
*/
CommentsFrame,

/**
* Indicates the frame is an event time code frame.
*/
EventTimeCodeFrame,

/**
* Indicates the frame is a genre frame.
*/
GenreFrame,

/**
* Indicates the frame is a music CD identifier frame.
*/
MusicCdIdentifierFrame,

/**
* Indicates the frame is a play count frame.
*/
PlayCountFrame,

/**
* Indicates the frame is a popularimeter frame.
*/
PopularimeterFrame,

/**
* Indicates the frame is a private frame.
*/
PrivateFrame,

/**
* Indicates the frame is relative volume frame.
*/
RelativeVolumeFrame,

/**
* Indicates the frame is a synchronized lyrics frame.
*/
SynchronizedLyricsFrame,

/**
* Indicates the frame is a terms of use frame.
*/
TermsOfUseFrame,

/**
* Indicates the frame is a text information frame.
*/
TextInformationFrame,

/**
* Indicates the frame is an unique file identifier frame.
*/
UniqueFileIdentifierFrame,

/**
* Indicates the frame is an unknown frame.
*/
UnknownFrame,

/**
* Indicates the frame is an attachment frame.
*/
UnsynchronizedLyricsFrame,

/**
* Indicates the frame is a URL link frame.
*/
UrlLinkFrame,

/**
* Indicates the frame is a user text information frame.
*/
UserTextInformationFrame,

/**
* Indicates the frame is a user URL link frame.
*/
UserUrlLinkFrame,
}

/**
* Abstract class that represents an ID3v2 frame. Frames are the unit for storing information in
* an ID3v2 tag. There are various types of frames that store differently structured information.
*/
export abstract class Frame {
export default abstract class Frame {

private _header: Id3v2FrameHeader;

Expand Down Expand Up @@ -150,12 +54,6 @@ export abstract class Frame {
// @TODO: This shouldn't be necessary, but removing it breaks more things than I want to fix right now.
public set flags(value: Id3v2FrameFlags) { this._header.flags = value; }

/**
* Gets a flag indicating which type of frame the current instance is.
*/
// @TODO: This can be removed as instanceof is pretty good now.
public abstract get frameClassType(): FrameClassType;

/**
* Gets the frame ID for the current instance.
* @returns Object representing of the identifier of the frame
Expand Down
2 changes: 1 addition & 1 deletion src/id3v2/frames/frameFactory.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import AttachmentFrame from "./attachmentFrame";
import CommentsFrame from "./commentsFrame";
import Frame from "./frame";
import GenreFrame from "./genreFrame";
import MusicCdIdentifierFrame from "./musicCdIdentifierFrame";
import PlayCountFrame from "./playCountFrame";
Expand All @@ -18,7 +19,6 @@ import {ByteVector} from "../../byteVector";
import {CorruptFileError, NotImplementedError} from "../../errors";
import {EventTimeCodeFrame} from "./eventTimeCodeFrame";
import {File} from "../../file";
import {Frame} from "./frame";
import {Id3v2FrameFlags, Id3v2FrameHeader} from "./frameHeader";
import {FrameIdentifier, FrameIdentifiers} from "../frameIdentifiers";
import {RelativeVolumeFrame} from "./relativeVolumeFrame";
Expand Down
17 changes: 4 additions & 13 deletions src/id3v2/frames/genreFrame.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import Frame from "./frame";
import Genres from "../../genres";
import Id3v2Settings from "../id3v2Settings";
import {ByteVector, StringType} from "../../byteVector";
import {Frame, FrameClassType} from "./frame";
import {Id3v2FrameHeader} from "./frameHeader";
import {FrameIdentifiers} from "../frameIdentifiers";
import {Guards, StringUtils} from "../../utils";
import {ArrayUtils, Guards, StringUtils} from "../../utils";
import {CorruptFileError} from "../../errors";

/**
Expand Down Expand Up @@ -135,9 +135,6 @@ export default class GenreFrame extends Frame {

// #region Properties

/** @inheritDoc */
public get frameClassType(): FrameClassType { return FrameClassType.GenreFrame; }

/**
* Gets the genres contained in the current instance.
* Note: Modifying the contents of the returned value will not modify the contents of the
Expand All @@ -163,15 +160,9 @@ export default class GenreFrame extends Frame {

// #region Public Methods

/**
* Gets a {@link GenreFrame} object from a specified list of genre frames.
* @param frames List of frames to search
* @returns Matching frame if it exists in `tag`, `undefined` if a matching frame was not found
*/
public static findGenreFrame(frames: GenreFrame[]): GenreFrame {
public static filterFrames(frames: Frame[]): GenreFrame[] {
Guards.truthy(frames, "frames");

return frames.find((f) => f.frameId === FrameIdentifiers.TCON);
return ArrayUtils.ofType(frames, GenreFrame);
}

/** @inheritDoc */
Expand Down
Loading