feat: implement music video NFO parser

Closes: #1261
This commit is contained in:
Christian Benincasa
2026-03-30 15:47:50 -04:00
parent 6833be2f1c
commit 4ffff3b649
6 changed files with 146 additions and 6 deletions

View File

@@ -0,0 +1,43 @@
import { readTestFile } from '../testing/util.ts';
import { MusicVideoNfoParser } from './MusicVideoNfoParser.ts';
describe('MusicVideo NFO parser', () => {
test('Kodi sample - ABBA', async () => {
const contents = (await readTestFile('abba_music_video.nfo')).toString(
'utf-8',
);
const result = await new MusicVideoNfoParser().parse(contents);
expect(result.isSuccess()).toBe(true);
const output = result.get().musicvideo;
expect(output).toMatchObject({
title: 'Dancing Queen',
artist: ['ABBA'],
album: 'Arrival',
genre: ['Pop'],
year: 1976,
});
expect(output.thumb).toHaveLength(2);
expect(output.plot).toContain('Dancing Queen');
});
test('Simple NFO example with minimal data', async () => {
const contents = (await readTestFile('music_video_2.nfo')).toString(
'utf-8',
);
const result = await new MusicVideoNfoParser().parse(contents);
expect(result.isSuccess()).toBe(true);
const output = result.get().musicvideo;
expect(output).toMatchObject({
title: 'MORE',
artist: ['Mark Osborne'],
album: 'YouTube',
premiered: '2013-03-03',
year: 2013,
});
expect(output.plot).toContain('Academy Award');
expect(output.genre).toBeUndefined();
expect(output.thumb).toBeUndefined();
});
});

View File

@@ -0,0 +1,16 @@
import { BaseNfoParser } from './BaseNfoParser.ts';
import { MusicVideoNfoContainer } from './NfoSchemas.ts';
const ArrayTags = ['musicvideo.artist', 'musicvideo.thumb', 'musicvideo.genre'];
export class MusicVideoNfoParser extends BaseNfoParser<
typeof MusicVideoNfoContainer
> {
constructor() {
super(MusicVideoNfoContainer, { parseTagValue: false });
}
protected override get arrayTags() {
return ArrayTags;
}
}

View File

@@ -8,7 +8,7 @@ export const NfoFieldWithAttrs = z.object({
export type NfoFieldWithAttrs = z.infer<typeof NfoFieldWithAttrs>;
export const NfoThumb = NfoFieldWithAttrs.extend({
const NfoThumb = NfoFieldWithAttrs.extend({
// '@_aspect': z.enum([
// 'banner',
// 'clearart',
@@ -18,10 +18,11 @@ export const NfoThumb = NfoFieldWithAttrs.extend({
// 'landscape',
// 'poster',
// ]),
'@_aspect': z.string(),
'@_aspect': z.string().optional(),
preview: z.string().optional(),
});
export const NfoUniqueId = NfoFieldWithAttrs.extend({
const NfoUniqueId = NfoFieldWithAttrs.extend({
'@_type': z.string(), //z.enum(['imdb', 'tmdb', 'tvdb']),
'@_default': z.stringbool().optional(),
});
@@ -32,7 +33,7 @@ export const NfoAudioStream = z.object({
channels: z.coerce.number(),
});
export const NfoVideoStream = z.object({
const NfoVideoStream = z.object({
codec: z.string().optional(),
aspect: z.coerce.number().or(z.string()).optional(),
width: z.coerce.number().optional(),
@@ -42,11 +43,11 @@ export const NfoVideoStream = z.object({
hdrtype: z.enum(['', 'hdr10', 'dolbyvision', 'hlg']).nullish().catch(''),
});
export const NfoSubtitleStream = z.object({
const NfoSubtitleStream = z.object({
language: z.string().optional(),
});
export const NfoFileInfo = z.object({
const NfoFileInfo = z.object({
streamdetails: z.object({
video: NfoVideoStream.optional(),
audio: z.array(NfoAudioStream).or(NfoAudioStream).optional(),
@@ -232,3 +233,18 @@ export function unwrapOtherVideoNfoContainer(
}
return head(container.episodedetails);
}
export const MusicVideoNfo = z.object({
title: z.string(),
artist: z.string().array(),
album: z.string().optional(),
plot: z.string().optional(),
premiered: z.string().optional(),
genre: z.array(z.string()).optional(),
thumb: NfoThumb.array().optional(),
year: z.coerce.number().int().optional(),
});
export const MusicVideoNfoContainer = z.object({
musicvideo: MusicVideoNfo,
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 B

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<musicvideo>
<title>Dancing Queen</title>
<userrating>0</userrating>
<top250>0</top250>
<track>-1</track>
<album>Arrival</album>
<outline></outline>
<plot>Dancing Queen est un des tubes emblématiques de l&apos;ère disco produits par le groupe suédois ABBA en 1976. Ce tube connaît un regain de popularité en 1994 lors de la sortie de Priscilla, folle du désert, et fait « presque » partie de la distribution du film Muriel.&#x0A;Le groupe a également enregistré une version espagnole de ce titre, La reina del baile, pour le marché d&apos;Amérique latine. On peut retrouver ces versions en espagnol des succès de ABBA sur l&apos;abum Oro. Le 18 juin 1976, ABBA a interprété cette chanson lors d&apos;un spectacle télévisé organisé en l&apos;honneur du roi Charles XVI Gustave de Suède, qui venait de se marier. Le titre sera repris en 2011 par Glee dans la saison 2, épisode 20.</plot>
<tagline></tagline>
<runtime>2</runtime>
<thumb preview="https://www.theaudiodb.com/images/media/album/thumb/arrival-4ee244732bbde.jpg/preview">https://www.theaudiodb.com/images/media/album/thumb/arrival-4ee244732bbde.jpg</thumb>
<thumb preview="https://assets.fanart.tv/preview/music/d87e52c5-bb8d-4da8-b941-9f4928627dc8/albumcover/arrival-548ab7a698b49.jpg">https://assets.fanart.tv/fanart/music/d87e52c5-bb8d-4da8-b941-9f4928627dc8/albumcover/arrival-548ab7a698b49.jpg</thumb>
<mpaa></mpaa>
<playcount>0</playcount>
<lastplayed></lastplayed>
<id></id>
<genre>Pop</genre>
<year>1976</year>
<status></status>
<director>Director 1</director>
<director>Director 2</director>
<director>Director 3</director>
<director>Director 4</director>
<code></code>
<aired></aired>
<trailer></trailer>
<fileinfo>
<streamdetails>
<video>
<codec>hevc</codec>
<aspect>1.792230</aspect>
<width>716</width>
<height>568</height>
<durationinseconds>143</durationinseconds>
<stereomode></stereomode>
</video>
<audio>
<codec>ac3</codec>
<language>eng</language>
<channels>2</channels>
</audio>
</streamdetails>
</fileinfo>
<artist>ABBA</artist>
<resume>
<position>0.000000</position>
<total>0.000000</total>
</resume>
<dateadded>2018-09-10 09:46:06</dateadded>
</musicvideo>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<musicvideo>
<title>MORE</title>
<artist>Mark Osborne</artist>
<album>YouTube</album>
<plot>Nominated for an Academy Award and awarded the Best Short Film at the 1999 Sundance Film Festival, MORE is a stop-motion mixed-media short film written and directed by Mark Osborne, the co-director of Kung Fu Panda and the upcoming animated feature film based on the iconic book The Little Prince.
MORE tells the story of an old, tired inventor as he struggles through joyless life in a drab and passionless society, leading the same cold and colorless existence accepted by the identical drones around him. At once tortured and inspired by his dreaming of and yearning for his younger carefree days, he struggles to finish the invention he hopes will give his life meaning and worth. His world, and the world of those around him, is transformed when his secret invention is completed. However, his subsequent success does not come without sacrifice. The inventor realizes that the true essence of his inspiration cannot be manufactured...
Available on DVD along with an hour long making of documentary called MAKING MORE at www.happyproduct.com
</plot>
<premiered>2013-03-03</premiered>
<year>2013</year>
</musicvideo>