package org.lodder.subtools.sublibrary.control;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.lodder.subtools.sublibrary.exception.ReleaseParseException;
import org.lodder.subtools.sublibrary.model.MovieRelease;
import org.lodder.subtools.sublibrary.model.Release;
import org.lodder.subtools.sublibrary.model.TvRelease;
import org.lodder.subtools.sublibrary.util.NamedMatcher;
import org.lodder.subtools.sublibrary.util.NamedPattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ReleaseParser {
private NamedMatcher namedMatcher;
private static VideoPatterns videoPatterns = new VideoPatterns();
private static final Logger LOGGER = LoggerFactory.getLogger(ReleaseParser.class);
@SuppressWarnings("unchecked")
public final Release parse(final File file) throws ReleaseParseException {
String foldername = "";
if (file.getParentFile() != null) foldername = file.getParentFile().getName();
String[] parsenames = new String[] {file.getName(), foldername};
for (String fileparsename : parsenames) {
for (NamedPattern np : videoPatterns.getCompiledPatterns()) {
namedMatcher = np.matcher(fileparsename);
if (namedMatcher.find()) {
LOGGER.trace("Parsing match found using file name: {}", fileparsename);
Object[] parseResults = parsePatternResult();
Release vFile = null;
if (parseResults.length == 4) {
vFile =
new TvRelease((String) parseResults[0], (Integer) parseResults[1],
(List<Integer>) parseResults[2], file,
extractFileNameExtension(file.getName()),
removeExtension((String) parseResults[3]), extractReleasegroup(file.getName(),
true), isSpecialEpisode((Integer) parseResults[1],
(List<Integer>) parseResults[2]));
} else if (parseResults.length == 3) {
vFile =
new MovieRelease((String) parseResults[0], (Integer) parseResults[1], file,
extractFileNameExtension(file.getName()),
removeExtension((String) parseResults[2]), extractReleasegroup(file.getName(),
true));
}
if (vFile == null) return vFile;
vFile.setQuality(getQualityKeyword(fileparsename));
return vFile;
}
}
}
throw new ReleaseParseException("Unknow format, can't be parsed: " + file.getAbsolutePath());
}
protected final Object[] parsePatternResult() throws ReleaseParseException {
List<String> namedgroups = namedMatcher.namedPattern().groupNames();
String seriesname = "";
List<Integer> episodenumbers = new ArrayList<Integer>();
int seasonnumber = 0;
int year = 0;
String description = "";
if (namedgroups.contains("description")) {
description = namedMatcher.group("description").substring(1);
}
if (namedgroups.contains("year")) {
year = Integer.parseInt(namedMatcher.group("year"));
}
if (namedgroups.contains("moviename")) {
if (namedgroups.contains("part")) {
String number = "";
if (namedgroups.contains("partnumber")) number = namedMatcher.group("partnumber");
if (namedgroups.contains("romanepisode")) number = namedMatcher.group("romanepisode");
return new Object[] {
cleanUnwantedChars(namedMatcher.group("moviename") + " " + namedMatcher.group("part")
+ " " + number), year, description};
} else {
return new Object[] {cleanUnwantedChars(namedMatcher.group("moviename")), year, description};
}
}
if (namedgroups.contains("episodenumber1")) {
LOGGER.trace("parsePatternResult: episodenumber1: {}", namedMatcher.group("episodenumber1"));
// Multiple episodes, have episodenumber1, 2 ....
for (String group : namedgroups) {
Pattern pattern = Pattern.compile("episodenumber(\\d+)");
Matcher match = pattern.matcher(group);
if (match.matches()) {
episodenumbers.add(Integer.parseInt(namedMatcher.group(group)));
}
}
Collections.sort(episodenumbers);
} else if (namedgroups.contains("episodenumberstart")) {
LOGGER.trace("parsePatternResult: episodenumberstart: {}",
namedMatcher.group("episodenumberstart"));
// Multiple episodes, regex specifies start and end number
int start = Integer.parseInt(namedMatcher.group("episodenumberstart"));
int end = Integer.parseInt(namedMatcher.group("episodenumberend"));
if (start > end) {
int temp = start;
start = end;
end = temp;
}
for (int i = start; i <= end; i++) {
episodenumbers.add(i);
}
} else if (namedgroups.contains("episodenumber")) {
LOGGER.trace("parsePatternResult: episodenumber: {}", namedMatcher.group("episodenumber"));
episodenumbers.add(Integer.parseInt(namedMatcher.group("episodenumber")));
} else if (namedgroups.contains("year") || namedgroups.contains("month")
|| namedgroups.contains("day")) {
// need to implement
} else if (namedgroups.contains("romanepisode") && !namedgroups.contains("year")) {
episodenumbers.add(Roman.decode(namedMatcher.group("romanepisode")));
}
if (namedgroups.contains("seriesname")) {
LOGGER.trace("parsePatternResult: seriesname: {}", namedMatcher.group("seriesname"));
seriesname = cleanUnwantedChars(namedMatcher.group("seriesname"));
if (namedgroups.contains("year")) {
seriesname = seriesname + " " + namedMatcher.group("year");
}
}
if (namedgroups.contains("seasonnumber")) {
LOGGER.trace("parsePatternResult: seasonnumber: {}", namedMatcher.group("seasonnumber"));
seasonnumber = Integer.parseInt(namedMatcher.group("seasonnumber"));
return new Object[] {seriesname, seasonnumber, episodenumbers, description};
} else if (namedgroups.contains("part") && !namedgroups.contains("year")) {
seasonnumber = 1;
return new Object[] {seriesname, seasonnumber, episodenumbers, description};
} else if (namedgroups.contains("year") && namedgroups.contains("month")
&& namedgroups.contains("day")) {
// need to implement
} else if (namedgroups.contains("season_episode")) {
LOGGER.trace("parsePatternResult: season_episode: {}", namedMatcher.group("season_episode"));
if (namedMatcher.group("season_episode").length() == 3) {
episodenumbers.add(Integer.parseInt(namedMatcher.group("season_episode").substring(1, 3)));
seasonnumber = Integer.parseInt(namedMatcher.group("season_episode").substring(0, 1));
} else if (namedMatcher.group("season_episode").length() == 4) {
episodenumbers.add(Integer.parseInt(namedMatcher.group("season_episode").substring(2, 4)));
seasonnumber = Integer.parseInt(namedMatcher.group("season_episode").substring(0, 2));
}
return new Object[] {seriesname, seasonnumber, episodenumbers, description};
} else {
// No season number specified, usually for Anime
// need to implement
}
throw new ReleaseParseException("Unable to parse the namedmatcher");
}
protected final String cleanUnwantedChars(String text) {
if (text.contains("cd1")) {
text = text.replace("cd1", " ");
}
if (text.contains("cd2")) {
text = text.replace("cd2", " ");
}
text = text.replace(".", " "); // remove point bones.01x01
text = text.replace("_", " "); // remove underscore bones_01x01
text = text.replace(" -", " "); // remove space dash "ncis - 01x01"
text = text.replace(":", ""); // remove double point "CSI: NY"
text = text.replace("(", ""); // remove ( for castle (2009)
text = text.replace(")", ""); // remove ) for castle (2009)
text = text.replace("'", "");
if (text.endsWith("-")) // implemented if for "hawaii five-0"
{
text = text.replace("-", ""); // remove space dash "altiplano-cd1"
}
// remove multiple spaces between text Back to the Future[][]Part II
text = text.replaceAll(" +", " ");
return text.trim();
}
public static final String getQualityKeyword(final String name) {
LOGGER.trace("getQualityKeyword: name: {}", name);
Pattern p = Pattern.compile(videoPatterns.getQualityKeysRegex(), Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(name);
StringBuilder builder = new StringBuilder();
while (m.find()) {
builder.append(m.group(0).replace(".", " ")).append(" ");
}
LOGGER.trace("getQualityKeyWords: keyswords: {}", builder.toString().trim());
return builder.toString().trim();
}
public static List<String> getQualityKeyWords(String name) {
LOGGER.trace("getQualityKeyWords: name: {}", name);
name = name.trim().toLowerCase();
Pattern p = Pattern.compile(videoPatterns.getQualityKeysRegex(), Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(name);
List<String> keywords = new ArrayList<>();
while (m.find()) {
keywords.add(m.group(0));
}
LOGGER.trace("getQualityKeyWords: keyswords: {}", keywords);
return keywords;
}
public static String extractFileNameExtension(final String fileName) {
int mid = fileName.lastIndexOf(".");
return fileName.substring(mid + 1, fileName.length());
}
public static String extractReleasegroup(final String fileName, boolean hasExtension) {
LOGGER.trace("extractReleasegroup: name: {} , hasExtension: {}", fileName, hasExtension);
Pattern releaseGroupPattern;
if (hasExtension) {
releaseGroupPattern = Pattern.compile("-([\\w]+).[\\w]+$");
} else {
releaseGroupPattern = Pattern.compile("-([\\w]+)$");
}
Matcher matcher = releaseGroupPattern.matcher(fileName);
String releaseGroup = "";
if (matcher.find()) {
releaseGroup = matcher.group(1);
}
LOGGER.trace("extractReleasegroup: release group: {}", releaseGroup);
return releaseGroup;
}
public static String removeExtension(final String fileName) {
final int index = fileName.lastIndexOf('.');
if (-1 == index) {
return fileName;
} else {
return fileName.substring(0, index);
}
}
public static boolean isSpecialEpisode(final int season, final List<Integer> episodeNumbers) {
if (season == 0) return true;
return episodeNumbers.size() == 1 && episodeNumbers.get(0) == 0;
}
}