All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.straylightlabs.tivolibre.PyTivoMetadata Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2015 Todd Kulesza .
 *
 * This file is part of TivoLibre. TivoLibre is derived from
 * TivoDecode 0.4.4 by Jeremy Drake. See the LICENSE-TivoDecode
 * file for the licensing terms for TivoDecode.
 *
 * TivoLibre is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * TivoLibre 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with TivoLibre.  If not, see .
 *
 */

package net.straylightlabs.tivolibre;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * Model PyTivo's metadata format, as documented at http://pytivo.sourceforge.net/wiki/index.php/Metadata.
 */
public class PyTivoMetadata {
    private final ZonedDateTime airDate;
    private final ZonedDateTime originalAirDate;
    private final int movieYear;
    private final String title;
    private final String seriesTitle;
    private final String episodeTitle;
    private final String description;
    private final boolean isEpisode;
    private final String seriesId;
    private final String programId;
    private final int episodeNumber;
//    private final int channelNumber;
//    private final String channelCallSign;
    private final int showingBits;
    private final int tvRating;
    private final int starRating;
    private final String mpaaRating;
    private final int colorCode;
    private final List programGenres;
    private final List actors;
    private final List guestStars;
    private final List directors;
    private final List execProducers;
    private final List producers;
    private final List writers;
    private final List hosts;
    private final List choreographers;
//    private final int partCount;
//    private final int partIndex;

    private final static Logger logger = LoggerFactory.getLogger(PyTivoMetadata.class);

    public static PyTivoMetadata createFromMetadata(List xmlDocs) {
        Builder builder = new Builder();

        boolean foundElement = false;
        for (Document doc : xmlDocs) {
            NodeList showings = doc.getElementsByTagName("showing");
            if (showings == null || foundElement) {
                continue;
            }

            builder = new Builder();
            for (int i = 0; i < showings.getLength() && !foundElement; i++) {
                Node node = showings.item(i);
                if (nodeEquals(node, "showing")) {
                    foundElement = parseShowingNode(node, builder);
                }
            }
        }

        return builder.build();
    }

    private static boolean nodeEquals(Node node, String name) {
        return node.getNodeName().equalsIgnoreCase(name);
    }

    private static boolean parseShowingNode(Node element, Builder builder) {
        boolean foundUniqueId = false;
        for (Node node = element.getFirstChild(); node != null; node = node.getNextSibling()) {
            if (nodeEquals(node, "showingBits")) {
                int showingBits = Integer.parseInt(node.getAttributes().getNamedItem("value").getNodeValue());
                builder.addShowingBits(showingBits);
            } else if (nodeEquals(node, "time")) {
                builder.airDate(node.getTextContent());
            } else if (nodeEquals(node, "tvRating")) {
                int rating = Integer.parseInt(node.getAttributes().getNamedItem("value").getNodeValue());
                builder.tvRating(rating);
            } else if (nodeEquals(node, "program")) {
                foundUniqueId = parseProgramNode(node, builder);
            }
        }
        return foundUniqueId;
    }

    private static boolean parseProgramNode(Node program, Builder builder) {
        boolean foundUniqueId = false;
        for (Node node = program.getFirstChild(); node != null; node = node.getNextSibling()) {
            if (nodeEquals(node, "showingBits")) {
                int showingBits = Integer.parseInt(node.getAttributes().getNamedItem("value").getNodeValue());
                builder.addShowingBits(showingBits);
            } else if (nodeEquals(node, "originalAirDate")) {
                builder.originalAirDate(node.getTextContent());
            } else if (nodeEquals(node, "title")) {
                builder.title(node.getTextContent());
            } else if (nodeEquals(node, "isEpisode")) {
                builder.isEpisode(node.getTextContent());
            } else if (nodeEquals(node, "episodeNumber")) {
                builder.episodeNumber(node.getTextContent());
            } else if (nodeEquals(node, "episodeTitle")) {
                builder.episodeTitle(node.getTextContent());
            } else if (nodeEquals(node, "movieYear")) {
                builder.movieYear(node.getTextContent());
            } else if (nodeEquals(node, "mpaaRating")) {
                builder.mpaaRating(node.getTextContent());
            } else if (nodeEquals(node, "starRating")) {
                builder.starRating(node.getAttributes().getNamedItem("value").getNodeValue());
            } else if (nodeEquals(node, "colorCode")) {
                builder.colorCode(node.getAttributes().getNamedItem("value").getNodeValue());
            } else if (nodeEquals(node, "description")) {
                builder.description(node.getTextContent());
            } else if (nodeEquals(node, "uniqueId")) {
                foundUniqueId = true;
                builder.programId(node.getTextContent());
            } else if (nodeEquals(node, "series")) {
                foundUniqueId |= parseSeriesNode(node, builder);
            } else if (nodeEquals(node, "vActor")) {
                parseListNode(node, builder.actors);
            } else if (nodeEquals(node, "vChoreographer")) {
                parseListNode(node, builder.choreographers);
            } else if (nodeEquals(node, "vDirector")) {
                parseListNode(node, builder.directors);
            } else if (nodeEquals(node, "vExecProducer")) {
                parseListNode(node, builder.execProducers);
            } else if (nodeEquals(node, "vProgramGenre")) {
                parseListNode(node, builder.programGenres);
            } else if (nodeEquals(node, "vGuestStar")) {
                parseListNode(node, builder.guestStars);
            } else if (nodeEquals(node, "vHost")) {
                parseListNode(node, builder.hosts);
            } else if (nodeEquals(node, "vProducer")) {
                parseListNode(node, builder.producers);
            } else if (nodeEquals(node, "vWriter")) {
                parseListNode(node, builder.writers);
            }
        }
        return foundUniqueId;
    }

    private static void parseListNode(Node listNode, List list) {
        for (Node node = listNode.getFirstChild(); node != null; node = node.getNextSibling()) {
            if (nodeEquals(node, "element")) {
                String val = node.getTextContent().trim();
                if (!val.isEmpty()) {
                    list.add(node.getTextContent());
                }
            }
        }
    }

    private static boolean parseSeriesNode(Node series, Builder builder) {
        boolean foundUniqueId = false;
        for (Node node = series.getFirstChild(); node != null; node = node.getNextSibling()) {
            if (nodeEquals(node, "seriesTitle")) {
                builder.seriesTitle(node.getTextContent());
            } else if (nodeEquals(node, "uniqueId")) {
                foundUniqueId = true;
                builder.seriesId(node.getTextContent());
            }
        }
        return foundUniqueId;
    }

    private PyTivoMetadata(Builder builder) {
        airDate = builder.airDate;
        originalAirDate = builder.originalAirDate;
        movieYear = builder.movieYear;
        if (builder.seriesTitle != null && builder.episodeTitle != null) {
            title = builder.seriesTitle + " - " + builder.episodeTitle;
        } else if (builder.title != null) {
            title = builder.title;
        } else if (builder.seriesTitle != null) {
            title = builder.seriesTitle;
        } else if (builder.episodeTitle != null) {
            title = builder.episodeTitle;
        } else {
            title = "Unknown";
        }
        seriesTitle = builder.seriesTitle;
        episodeTitle = builder.episodeTitle;
        description = builder.description;
        isEpisode = builder.isEpisode;
        seriesId = builder.seriesId;
        programId = builder.programId;
        episodeNumber = builder.episodeNumber;
//        channelNumber = builder.channelNumber;
//        channelCallSign = builder.channelCallSign;
        showingBits = builder.showingBits;
        tvRating = builder.tvRating;
        starRating = builder.starRating;
        mpaaRating = builder.mpaaRating;
        colorCode = builder.colorCode;
        programGenres = new ArrayList<>(builder.programGenres);
        actors = new ArrayList<>(builder.actors);
        guestStars = new ArrayList<>(builder.guestStars);
        directors = new ArrayList<>(builder.directors);
        execProducers = new ArrayList<>(builder.execProducers);
        producers = new ArrayList<>(builder.producers);
        writers = new ArrayList<>(builder.writers);
        hosts = new ArrayList<>(builder.hosts);
        choreographers = new ArrayList<>(builder.choreographers);
//        partCount = builder.partCount;
//        partIndex = builder.partIndex;
    }

    public boolean writeToFile(Path filePath) {
        try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(filePath, StandardOpenOption.CREATE, StandardOpenOption.WRITE))) {
            writeString(writer, "title", title);
            writeString(writer, "seriesTitle", seriesTitle);
            writeString(writer, "episodeTitle", episodeTitle);
            writeString(writer, "time", airDate);
            writeString(writer, "originalAirDate", originalAirDate);
            writeString(writer, "description", description);
            writeString(writer, "isEpisode", isEpisode);
            writeString(writer, "seriesId", seriesId);
            writeString(writer, "programId", programId);
            writeString(writer, "mpaaRating", mpaaRating);

            writeInt(writer, "episodeNumber", episodeNumber);
            writeInt(writer, "tvRating", tvRating, "x");
            writeInt(writer, "starRating", starRating, "x");
            writeInt(writer, "showingBits", showingBits);
            writeInt(writer, "colorCode", colorCode, "x");
//            if (partCount > 0 && partIndex > 0) {
//                writeInt(writer, "partCount", partCount);
//                writeInt(writer, "partIndex", partIndex);
//            }
            writeInt(writer, "movieYear", movieYear);

            writeList(writer, "vActor", actors);
            writeList(writer, "vProgramGenre", programGenres);
            writeList(writer, "vDirector", directors);
            writeList(writer, "vGuestStar", guestStars);
            writeList(writer, "vExecProducer", execProducers);
            writeList(writer, "vProducer", producers);
            writeList(writer, "vWriter", writers);
            writeList(writer, "vHost", hosts);
            writeList(writer, "vChoreographer", choreographers);

        } catch (IOException e) {
            logger.error("Error writing to file '{}': ", filePath, e);
            return false;
        }

        return true;
    }

    private void writeString(PrintWriter writer, String field, Object val) {
        if (val != null) {
            writer.format("%s : %s%n", field, val);
        }
    }

    private void writeInt(PrintWriter writer, String field, int val) {
        writeInt(writer, field, val, "");
    }

    private void writeInt(PrintWriter writer, String field, int val, String prefix) {
        if (val > 0) {
            writer.format("%s : %s%d%n", field, prefix, val);
        }
    }

    private void writeList(PrintWriter writer, String field, List list) {
        if (list.size() > 0) {
            list.stream().forEach(e -> writer.format("%s : %s%n", field, e));
        }
    }

    static class Builder {
        private ZonedDateTime airDate;
        private ZonedDateTime originalAirDate;
        private int movieYear;
        private String title;
        private String episodeTitle;
        private String description;
        private boolean isEpisode;
        private String seriesId;
        private String programId;
        private String seriesTitle;
        private int episodeNumber;
//        private int channelNumber;
//        private String channelCallSign;
        private int showingBits;
        private int tvRating;
        private int starRating;
        private String mpaaRating;
        private int colorCode;
        private List programGenres;
        private List actors;
        private List guestStars;
        private List directors;
        private List execProducers;
        private List producers;
        private List writers;
        private List hosts;
        private List choreographers;
//        private int partCount;
//        private int partIndex;

        public Builder() {
            programGenres = new ArrayList<>();
            actors = new ArrayList<>();
            guestStars = new ArrayList<>();
            directors = new ArrayList<>();
            execProducers = new ArrayList<>();
            producers = new ArrayList<>();
            writers = new ArrayList<>();
            hosts = new ArrayList<>();
            choreographers = new ArrayList<>();
        }

        public PyTivoMetadata build() {
            return new PyTivoMetadata(this);
        }

        public Builder title(String title) {
            this.title = title;
            return this;
        }

        public Builder addShowingBits(int bits) {
            showingBits += bits;
            return this;
        }

        public Builder airDate(String date) {
            airDate = ZonedDateTime.parse(date);
            return this;
        }

        public Builder originalAirDate(String date) {
            originalAirDate = ZonedDateTime.parse(date);
            return this;
        }

        public Builder isEpisode(String val) {
            isEpisode = Boolean.parseBoolean(val);
            return this;
        }

        public Builder colorCode(String val) {
            colorCode = Integer.parseInt(val);
            return this;
        }

        public Builder seriesId(String val) {
            seriesId = val;
            return this;
        }

        public Builder programId(String val) {
            programId = val;
            return this;
        }

        public Builder seriesTitle(String val) {
            seriesTitle = val;
            return this;
        }

        public Builder description(String val) {
            description = val;
            return this;
        }

        public Builder episodeNumber(String val) {
            episodeNumber = Integer.parseInt(val);
            return this;
        }

        public Builder episodeTitle(String val) {
            episodeTitle = val;
            return this;
        }

        public Builder tvRating(int val) {
            tvRating = val;
            return this;
        }

        public Builder movieYear(String val) {
            movieYear = Integer.parseInt(val);
            return this;
        }

        public Builder starRating(String val) {
            starRating = Integer.parseInt(val);
            return this;
        }

        public Builder mpaaRating(String val) {
            mpaaRating = val;
            return this;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy