• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

benrr101 / node-taglib-sharp / 46462135

pending completion
46462135

push

appveyor

Benjamin Russell
Merge branch 'release/v5.1.0'

3096 of 3788 branches covered (81.73%)

Branch coverage included in aggregate %.

2171 of 2171 new or added lines in 47 files covered. (100.0%)

25320 of 26463 relevant lines covered (95.68%)

437.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.17
/src/id3v2/frames/unsynchronizedLyricsFrame.ts
1
import Id3v2Settings from "../id3v2Settings";
1✔
2
import {ByteVector, StringType} from "../../byteVector";
1✔
3
import {CorruptFileError} from "../../errors";
1✔
4
import {Frame, FrameClassType} from "./frame";
1✔
5
import {Id3v2FrameHeader} from "./frameHeader";
1✔
6
import {FrameIdentifiers} from "../frameIdentifiers";
1✔
7
import {Guards} from "../../utils";
1✔
8

9
/**
10
 * Extends {@link Frame} implementing support for ID3v2 unsynchronized lyrics (USLT) frames.
11
 */
12
export default class UnsynchronizedLyricsFrame extends Frame {
1✔
13
    private _description: string;
14
    private _language: string;
15
    private _text: string;
16
    private _textEncoding: StringType = Id3v2Settings.defaultEncoding;
50✔
17

18
    // #region Constructors
19

20
    private constructor(header: Id3v2FrameHeader) {
21
        super(header);
50✔
22
    }
23

24
    /**
25
     * Constructs and initializes a new instance from the provided data
26
     * @param description Description of the frame
27
     * @param language ISO-639-2 language code for the content of the frame
28
     * @param encoding Encoding to use when storing the content of the frame
29
     */
30
    public static fromData(
31
        description: string,
32
        language?: string,
33
        encoding: StringType = Id3v2Settings.defaultEncoding
2✔
34
    ): UnsynchronizedLyricsFrame {
35
        const frame = new UnsynchronizedLyricsFrame(new Id3v2FrameHeader(FrameIdentifiers.USLT));
4✔
36
        frame.textEncoding = encoding;
4✔
37
        frame._language = language;
4✔
38
        frame._description = description;
4✔
39
        return frame;
4✔
40
    }
41

42
    /**
43
     * Constructs and initializes a new instance by reading its raw data in a specified ID3v2
44
     * version. This method allows for offset reading from the data byte vector.
45
     * @param data Raw representation of the new frame
46
     * @param offset What offset in `data` the frame actually begins. Must be positive,
47
     *     safe integer
48
     * @param header Header of the frame found at `data` in the data
49
     * @param version ID3v2 version the frame was originally encoded with
50
     */
51
    public static fromOffsetRawData(
52
        data: ByteVector,
53
        offset: number,
54
        header: Id3v2FrameHeader,
55
        version: number
56
    ): UnsynchronizedLyricsFrame {
57
        Guards.truthy(data, "data");
12✔
58
        Guards.uint(offset, "offset");
10✔
59
        Guards.truthy(header, "header");
5✔
60
        Guards.byte(version, "version");
3✔
61

62
        const frame = new UnsynchronizedLyricsFrame(header);
3✔
63
        frame.setData(data, offset, false, version);
3✔
64
        return frame;
3✔
65
    }
66

67
    /**
68
     * Constructs and initializes a new instance by reading its raw data in a specified
69
     * ID3v2 version.
70
     * @param data Raw representation of the new frame
71
     * @param version ID3v2 version the raw frame is encoded with, must be a positive 8-bit integer
72
     */
73
    public static fromRawData(data: ByteVector, version: number): UnsynchronizedLyricsFrame {
74
        Guards.truthy(data, "data");
48✔
75
        Guards.byte(version, "version");
46✔
76

77
        const frame = new UnsynchronizedLyricsFrame(Id3v2FrameHeader.fromData(data, version));
43✔
78
        frame.setData(data, 0, true, version);
43✔
79
        return frame;
43✔
80
    }
81

82
    // #endregion
83

84
    // #region Properties
85

86
    /** @inheritDoc */
87
    public get frameClassType(): FrameClassType { return FrameClassType.UnsynchronizedLyricsFrame; }
10✔
88

89
    /**
90
     * Gets the description of the contents of the current instance.
91
     */
92
    public get description(): string { return this._description || ""; }
43✔
93
    /**
94
     * Sets the description of the contents of the current instance.
95
     * There should only be one frame with a this description and ISO-639-2 code per tag.
96
     */
97
    public set description(value: string) { this._description = value; }
12✔
98

99
    /**
100
     * Gets the ISO-639-2 language code for the contents of this instance.
101
     */
102
    public get language(): string { return (this._language && this._language.length === 3) ? this._language : "XXX"; }
34✔
103
    /**
104
     * Sets the ISO-639-2 language code for the contents of this instance.
105
     */
106
    public set language(value: string) { this._language = value; }
15✔
107

108
    /**
109
     * Gets the text stored in the current instance.
110
     */
111
    public get text(): string { return this._text || ""; }
14✔
112
    /**
113
     * Sets the text stored in the current instance.
114
     */
115
    public set text(value: string) { this._text = value; }
4✔
116

117
    /**
118
     * Gets the text encoding to use when storing the current instance.
119
     */
120
    public get textEncoding(): StringType { return this._textEncoding; }
58✔
121
    /**
122
     * Sets the text encoding to use when storing the current instance.
123
     */
124
    public set textEncoding(value: StringType) { this._textEncoding = value; }
52✔
125

126
    // #endregion
127

128
    // #region Public Methods
129

130
    /**
131
     * Gets the first unsynchronized lyrics frame from a list of frames that matches the provided
132
     * parameters.
133
     * @param frames List of frames to search
134
     * @param description Description to match
135
     * @param language Optionally, ISO-639-2 language code to match
136
     * @returns Frame that matches provided parameters, `undefined` if a match was not found
137
     */
138
    public static find(
139
        frames: UnsynchronizedLyricsFrame[],
140
        description: string,
141
        language: string
142
    ): UnsynchronizedLyricsFrame {
143
        Guards.truthy(frames, "frames");
8✔
144
        return frames.find((f) => {
6✔
145
            if (f.description !== description) { return false; }
6✔
146
            // noinspection RedundantIfStatementJS
147
            if (language && f.language !== language) { return false; }
4✔
148
            return true;
2✔
149
        });
150
    }
151

152
    /**
153
     * Gets all unsynchronized lyrics frames that match the provided parameters from a list of
154
     * frames
155
     * @param frames List of frames to search
156
     * @param description Description to match
157
     * @param language Optionally, ISO-639-2 language code to match
158
     * @returns List of frames matching provided parameters, empty array if no matches were found
159
     */
160
    public static findAll(
161
        frames: UnsynchronizedLyricsFrame[],
162
        description: string,
163
        language: string
164
    ): UnsynchronizedLyricsFrame[] {
165
        Guards.truthy(frames, "frames");
7✔
166
        return frames.filter((f) => {
5✔
167
            if (f.description !== description) { return false; }
12✔
168
            // noinspection RedundantIfStatementJS
169
            if (language && f.language !== language) { return false; }
8✔
170
            return true;
5✔
171
        });
172
    }
173

174
    /**
175
     * Gets a specified unsynchronized frame from the list of frames, trying to match the
176
     * description and language but, failing a perfect match, accepting an incomplete match.
177
     * The method tries matching with the following order of precedence:
178
     * * First frame with a matching description and language
179
     * * First frame with a matching language
180
     * * First frame with a matching description
181
     * * First frame
182
     * @param frames List of frames to search
183
     * @param description Description to match
184
     * @param language ISO-639-2 language code to match
185
     */
186
    public static findPreferred(
187
        frames: UnsynchronizedLyricsFrame[],
188
        description: string,
189
        language: string
190
    ): UnsynchronizedLyricsFrame {
191
        Guards.truthy(frames, "frames");
18✔
192

193
        let bestValue = -1;
16✔
194
        let bestFrame;
195
        for (const f of frames) {
16✔
196
            const sameName = f.description === description;
12✔
197
            const sameLang = f.language === language;
12✔
198

199
            if (sameName && sameLang) {
12✔
200
                return f;
2✔
201
            }
202

203
            const value = sameLang
10✔
204
                ? 2
10✔
205
                : sameName ? 1 : 0;
8✔
206
            if (value > bestValue) {
10✔
207
                bestValue = value;
9✔
208
                bestFrame = f;
9✔
209
            }
210
        }
211

212
        return bestFrame;
14✔
213
    }
214

215
    /** @inheritDoc */
216
    public clone(): Frame {
217
        const frame = UnsynchronizedLyricsFrame.fromData(this._description, this._language, this.textEncoding);
1✔
218
        frame._text = this._text;
1✔
219
        return frame;
1✔
220
    }
221

222
    /**
223
     * Generates a string representation of the current instance.
224
     */
225
    public toString(): string {
226
        return this.text;
1✔
227
    }
228

229
    /** @inheritDoc */
230
    protected parseFields(data: ByteVector): void {
231
        if (data.length < 4) {
46!
232
            throw new CorruptFileError("Not enough bytes in field.");
×
233
        }
234

235
        this.textEncoding = data.get(0);
46✔
236
        this._language = data.subarray(1, 3).toString(StringType.Latin1);
46✔
237

238
        const split = data.subarray(4).toStrings(this.textEncoding, 2);
46✔
239
        if (split.length === 1) {
46✔
240
            // Bad lyrics frame. Assume that it lacks a description
241
            this._description = "";
3✔
242
            this._text = split[0];
3✔
243
        } else {
244
            this._description = split[0];
43✔
245
            this._text = split[1];
43✔
246
        }
247
    }
248

249
    /** @inheritDoc */
250
    protected renderFields(version: number): ByteVector {
251
        const encoding = UnsynchronizedLyricsFrame.correctEncoding(this.textEncoding, version);
2✔
252
        return ByteVector.concatenate(
2✔
253
            encoding,
254
            ByteVector.fromString(this.language, StringType.Latin1),
255
            ByteVector.fromString(this.description, encoding),
256
            ByteVector.getTextDelimiter(encoding),
257
            ByteVector.fromString(this.text, encoding)
258
        );
259
    }
260

261
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc