Task 451: .NFO File Format
Task 451: .NFO File Format
File Format Specifications for the .NFO File Format
The .NFO file format is a plain text file, but in the context of media management software like Kodi, it is structured as an XML file containing metadata for movies, TV shows, music, or other media. There is no formal "specification" in the sense of a binary format, but the structure for Kodi-compatible .NFO files is documented in the Kodi wiki. It uses XML tags to store properties such as title, plot, ratings, and more. For this task, I'll focus on the movie .NFO structure, as it is a common use case and has a well-defined set of properties. The file starts with and has a root element for movies.
- List of all the properties of this file format intrinsic to its file system:
- title: The title of the movie.
- originaltitle: The original title of the movie.
- sorttitle: Alternative title for sorting.
- ratings: Container for ratings, with sub-properties: rating (with attributes name, max, default), value, votes.
- userrating: User's personal rating.
- top250: IMDB Top 250 ranking.
- outline: Short plot summary.
- plot: Full plot description.
- tagline: Movie slogan.
- runtime: Movie duration in minutes.
- thumb: Path to poster artwork (attributes: aspect, preview).
- fanart: Container for fanart images, with sub-property thumb (attribute: preview).
- mpaa: MPAA rating.
- playcount: Number of times played.
- lastplayed: Date last played (yyyy-mm-dd).
- uniqueid: Unique identifier (attributes: type, default).
- genre: Movie genre (multiple allowed).
- tag: Library tags (multiple allowed).
- set: Movie set information, with sub-properties: name, overview.
- country: Country of origin (multiple allowed).
- credits: Writers (multiple allowed).
- director: Director (multiple allowed).
- premiered: Release date (yyyy-mm-dd).
- year: Release year (deprecated, use premiered).
- status: Not used.
- code: Not used.
- aired: Not used.
- studio: Production studio (multiple allowed).
- trailer: Path to trailer.
- fileinfo: Container for stream details, with sub-properties: streamdetails, which contains video (sub-properties: codec, aspect, width, height, durationinseconds, stereomode, hdrtype), audio (sub-properties: codec, language, channels; multiple allowed), subtitle (sub-property: language; multiple allowed).
- actor: Actor information (multiple allowed), with sub-properties: name, role, order, thumb.
- showlink: Link to TV show title if applicable.
- resume: Resume point, with sub-properties: position, total (in seconds).
- dateadded: Date the file was added (based on mTime or configurable).
These properties are XML elements or attributes within the root tag. Some are required (e.g., title), others optional, and some allow multiple instances.
- Two direct download links for files of format .NFO:
- https://gist.githubusercontent.com/RichieChill/425342cbbb2e5ebc77ec/raw
- https://raw.githubusercontent.com/razielanarki/arduino_dsmr_p1_reader/master/example_output.nfo (Note: This is a sample output .nfo from a different context, but it's a valid .nfo file; for Kodi-specific, the first link is a movie template.)
- Ghost blog embedded html javascript that allows a user to drag n drop a file of format .NFO and it will dump to screen all these properties.
Here is an HTML page with embedded JavaScript for drag-and-drop .NFO file upload. It parses the .NFO as XML and extracts the properties listed above, displaying them on the screen.
Drag and Drop .NFO File
- Python class that can open any file of format .NFO and decode read and write and print to console all the properties from the above list.
import xml.etree.ElementTree as ET
import os
class NFOHandler:
def __init__(self, filepath):
self.filepath = filepath
self.tree = None
self.root = None
self.properties = [
'title', 'originaltitle', 'sorttitle', 'ratings', 'userrating', 'top250', 'outline', 'plot', 'tagline', 'runtime',
'thumb', 'fanart', 'mpaa', 'playcount', 'lastplayed', 'uniqueid', 'genre', 'tag', 'set', 'country', 'credits',
'director', 'premiered', 'year', 'status', 'code', 'aired', 'studio', 'trailer', 'fileinfo', 'actor', 'showlink',
'resume', 'dateadded'
]
self.load()
def load(self):
if os.path.exists(self.filepath):
self.tree = ET.parse(self.filepath)
self.root = self.tree.getroot()
else:
self.root = ET.Element('movie')
self.tree = ET.ElementTree(self.root)
def read_properties(self):
prop_dict = {}
for prop in self.properties:
elements = self.root.findall(prop)
if elements:
prop_dict[prop] = []
for elem in elements:
data = {'text': elem.text.strip() if elem.text else None}
data['attributes'] = dict(elem.attrib)
data['children'] = [child.tag for child in elem]
prop_dict[prop].append(data)
return prop_dict
def print_properties(self):
props = self.read_properties()
for prop, values in props.items():
print(f"{prop}:")
for val in values:
print(f" Text: {val['text']}")
print(f" Attributes: {val['attributes']}")
print(f" Children: {val['children']}")
if not values:
print(" Not found")
def write_property(self, prop, value, attributes=None, children=None):
elem = self.root.find(prop)
if elem is None:
elem = ET.SubElement(self.root, prop)
elem.text = value
if attributes:
for k, v in attributes.items():
elem.set(k, v)
if children:
for child_tag, child_value in children.items():
child = ET.SubElement(elem, child_tag)
child.text = child_value
self.save()
def save(self):
self.tree.write(self.filepath, encoding='utf-8', xml_declaration=True)
# Example usage
# nfo = NFOHandler('sample.nfo')
# nfo.print_properties()
# nfo.write_property('title', 'Test Movie', attributes={'lang': 'en'})
For closed-ended math questions, but this is not math.
- Java class that can open any file of format .NFO and decode read and write and print to console all the properties from the above list.
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.io.File;
public class NFOHandler {
private String filepath;
private Document doc;
private String[] properties = {
"title", "originaltitle", "sorttitle", "ratings", "userrating", "top250", "outline", "plot", "tagline", "runtime",
"thumb", "fanart", "mpaa", "playcount", "lastplayed", "uniqueid", "genre", "tag", "set", "country", "credits",
"director", "premiered", "year", "status", "code", "aired", "studio", "trailer", "fileinfo", "actor", "showlink",
"resume", "dateadded"
};
public NFOHandler(String filepath) {
this.filepath = filepath;
load();
}
private void load() {
try {
File file = new File(filepath);
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
if (file.exists()) {
doc = dBuilder.parse(file);
doc.getDocumentElement().normalize();
} else {
doc = dBuilder.newDocument();
Element root = doc.createElement("movie");
doc.appendChild(root);
}
} catch (Exception e) {
e.printStack.printStackTrace();
}
}
public void printProperties() {
for (String prop : properties) {
System.out.println(prop + ":");
NodeList nodes = doc.getElementsByTagName(prop);
if (nodes.getLength() > 0) {
for (int i = 0; i < nodes.getLength(); i++) {
Element elem = (Element) nodes.item(i);
System.out.println(" Text: " + (elem.getTextContent() != null ? elem.getTextContent().trim() : "null"));
if (elem.hasAttributes()) {
System.out.println(" Attributes: " + elem.getAttributes().item(0).toString()); // Simplified
}
if (elem.hasChildNodes()) {
System.out.println(" Children: " + elem.getFirstChild().getNodeName());
}
}
} else {
System.out.println(" Not found");
}
}
}
public void writeProperty(String prop, String value, String attributeKey, String attributeValue) {
Element elem = (Element) doc.getElementsByTagName(prop).item(0);
if (elem == null) {
elem = doc.createElement(prop);
doc.getDocumentElement().appendChild(elem);
}
elem.setTextContent(value);
if (attributeKey != null) {
elem.setAttribute(attributeKey, attributeValue);
}
save();
}
private void save() {
try {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File(filepath));
transformer.transform(source, result);
} catch (Exception e) {
e.printStackTrace();
}
}
// Example usage
// public static void main(String[] args) {
// NFOHandler nfo = new NFOHandler("sample.nfo");
// nfo.printProperties();
// nfo.writeProperty("title", "Test Movie", "lang", "en");
// }
}
- Javascript class that can open any file of format .NFO and decode read and write and print to console all the properties from the above list.
Note: JavaScript in browser can't directly open files from filesystem without user input, so this assumes a Node.js environment with 'fs' and 'xml2js' module (install via npm).
const fs = require('fs');
const xml2js = require('xml2js');
class NFOHandler {
constructor(filepath) {
this.filepath = filepath;
this.properties = [
'title', 'originaltitle', 'sorttitle', 'ratings', 'userrating', 'top250', 'outline', 'plot', 'tagline', 'runtime',
'thumb', 'fanart', 'mpaa', 'playcount', 'lastplayed', 'uniqueid', 'genre', 'tag', 'set', 'country', 'credits',
'director', 'premiered', 'year', 'status', 'code', 'aired', 'studio', 'trailer', 'fileinfo', 'actor', 'showlink',
'resume', 'dateadded'
];
this.data = null;
this.load();
}
load() {
if (fs.existsSync(this.filepath)) {
const xml = fs.readFileSync(this.filepath, 'utf8');
xml2js.parseString(xml, (err, result) => {
if (err) {
console.error(err);
} else {
this.data = result.movie || result;
}
});
} else {
this.data = {};
}
}
readProperties() {
const props = {};
this.properties.forEach(prop => {
props[prop] = this.data[prop] || 'Not found';
});
return props;
}
printProperties() {
const props = this.readProperties();
for (const [prop, value] of Object.entries(props)) {
console.log(`${prop}: ${Array.isArray(value) ? value.join(', ') : value}`);
}
}
writeProperty(prop, value) {
this.data[prop] = [value]; // Simple, assumes single value
const builder = new xml2js.Builder();
const xml = builder.buildObject({movie: this.data});
fs.writeFileSync(this.filepath, xml);
}
}
// Example usage
// const nfo = new NFOHandler('sample.nfo');
// nfo.printProperties();
// nfo.writeProperty('title', 'Test Movie');
- C class that can open any file of format .NFO and decode read and write and print to console all the properties from the above list.
Note: C doesn't have built-in XML parsing, so this uses a simple string search for demonstration. For real use, link with libxml2.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_LINE 1024
const char* properties[] = {
"title", "originaltitle", "sorttitle", "ratings", "userrating", "top250", "outline", "plot", "tagline", "runtime",
"thumb", "fanart", "mpaa", "playcount", "lastplayed", "uniqueid", "genre", "tag", "set", "country", "credits",
"director", "premiered", "year", "status", "code", "aired", "studio", "trailer", "fileinfo", "actor", "showlink",
"resume", "dateadded",
NULL
};
typedef struct {
char* filepath;
} NFOHandler;
NFOHandler* create_nfo_handler(const char* filepath) {
NFOHandler* handler = malloc(sizeof(NFOHandler));
handler->filepath = strdup(filepath);
return handler;
}
void print_properties(NFOHandler* handler) {
FILE* fp = fopen(handler->filepath, "r");
if (fp == NULL) {
printf("File not found\n");
return;
}
char line[MAX_LINE];
while (fgets(line, MAX_LINE, fp)) {
for (int i = 0; properties[i]; i++) {
char tag_open[50];
snprintf(tag_open, 50, "<%s>", properties[i]);
if (strstr(line, tag_open)) {
char* start = strstr(line, ">") + 1;
char* end = strstr(start, "</");
if (end) {
*end = '\0';
printf("%s: %s\n", properties[i], start);
}
}
}
}
fclose(fp);
}
void write_property(NFOHandler* handler, const char* prop, const char* value) {
FILE* fp = fopen(handler->filepath, "a");
if (fp == NULL) {
fp = fopen(handler->filepath, "w");
}
fprintf(fp, "<%s>%s</%s>\n", prop, value, prop);
fclose(fp);
}
void destroy_nfo_handler(NFOHandler* handler) {
free(handler->filepath);
free(handler);
}
// Example usage
// int main() {
// NFOHandler* nfo = create_nfo_handler("sample.nfo");
// print_properties(nfo);
// write_property(nfo, "title", "Test Movie");
// destroy_nfo_handler(nfo);
// return 0;
// }