/* * Copyright (C) 2014 Michell Bak * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.miz.identification; import android.text.TextUtils; import com.miz.functions.MizLib; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ShowStructure { private final String mFilepath; private String mFilename, mSeasonFolder = "", mShowFolder = "", mImdbId, mCustomTags = ""; private int mSeasonFolderNumber; private ArrayList<Episode> mEpisodes = new ArrayList<Episode>(); public ShowStructure(String filepath) { mFilepath = filepath; split(); checkImdbId(); mEpisodes = new ArrayList<Episode>(decryptEpisodes(getFilename())); seasonFolderOverride(); } public void setCustomTags(String customTags) { mCustomTags = customTags; } public void split() { Pattern splitPattern = Pattern.compile("/"); // Pre-compiled pattern to speed things up String[] split = splitPattern.split(mFilepath.contains("<MiZ>") ? mFilepath.split("<MiZ>")[0] : mFilepath); if (split.length >= 3) { mFilename = split[split.length - 1]; // Check if it's a valid season folder (returns -1 if it's not) int tempSeasonCheck = getSeasonFolderNumber(split[split.length - 2].trim()); if (tempSeasonCheck >= 0) { // It's valid! Use it as the season folder // and its parent folder as the show folder mSeasonFolder = split[split.length - 2].trim(); mSeasonFolderNumber = tempSeasonCheck; mShowFolder = split[split.length - 3].trim(); } else { // It's not... Use it as the show folder mShowFolder = split[split.length - 2].trim(); } } else { // We're dealing with two or less parts mFilename = split[split.length - 1].trim(); if (split.length == 2) { mShowFolder = split[0].trim(); } } } public void checkImdbId() { // Prioritize the show folder IMDB ID if (hasShowFolder()) { String temp = MizLib.decryptImdbId(getShowFolderName()); if (null != temp) { mImdbId = temp; return; } } String temp = MizLib.decryptImdbId(getFilename()); if (null != temp) mImdbId = temp; } public void seasonFolderOverride() { if (hasSeasonFolder()) { for (int i = 0; i < mEpisodes.size(); i++) mEpisodes.get(i).setSeason(mSeasonFolderNumber); } } public String getFilepath() { return mFilepath; } public String getFilename() { return mFilename; } public String getDecryptedFilename() { if (mEpisodes.size() > 0) return MizLib.decryptName(mEpisodes.get(0).getBefore(), mCustomTags); return MizLib.decryptName(getFilename(), mCustomTags); } public boolean hasSeasonFolder() { return !TextUtils.isEmpty(getSeasonFolderName()); } public String getSeasonFolderName() { return mSeasonFolder; } public boolean hasShowFolder() { return !TextUtils.isEmpty(getShowFolderName()); } public String getDecryptedShowFolderName() { return MizLib.decryptName(getShowFolderName(), mCustomTags); } public String getShowFolderName() { return mShowFolder; } public boolean hasImdbId() { return null != getImdbId(); } public String getImdbId() { return mImdbId; } public int getSeasonFolderNumber() { return mSeasonFolderNumber; } public int getSeasonFolderNumber(String folderName) { folderName = folderName.trim(); // Season ## or Season## [1-4] [has to begin with it] Pattern pattern = Pattern.compile("^(?:season|staffel|series)[-_ \\.]?(\\d{1,4}).*?$", Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(folderName); if (matcher.find()) return MizLib.getInteger(matcher.group(1)); // S## or S ## [1-4] [has to begin with it] pattern = Pattern.compile("^s[-_ \\.]?(\\d{1,4}).*?$", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(folderName); if (matcher.find()) return MizLib.getInteger(matcher.group(1)); // ## [1-4] [has to contain just that] pattern = Pattern.compile("^(\\d{1,4})$", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(folderName); if (matcher.find()) return MizLib.getInteger(matcher.group(1)); // ## season / staffel / series [1-4] [has to contain just that] pattern = Pattern.compile("^(\\d{1,4})[-_ \\.]?(?:season|staffel|series)$", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(folderName); if (matcher.find()) return MizLib.getInteger(matcher.group(1)); // special / specials / special episode / special episodes [has to contain just that] pattern = Pattern.compile("^(([s][p][e][c][i][a][l](?:([s]*)|([-_ \\.]?[e][p][i][s][o][d][e][s]*)))|([e][x][t][r][a][s]*))$", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(folderName); if (matcher.find()) return 0; // Specials use 0 as the season number return -1; } public ArrayList<Episode> getEpisodes() { return mEpisodes; } /** * Get release year from show folder name (priority) or file name. * @return Release year if found, -1 otherwise. */ public int getReleaseYear() { if (mEpisodes.size() == 0) return -1; Pattern pattern = Pattern.compile("^.*?((?:18|19|20)[0-9][0-9]).*?$"); // Attempt to match it against the show folder name first Matcher matcher = pattern.matcher(getShowFolderName()); if (matcher.find()) return MizLib.getInteger(matcher.group(1)); // Check if there's a release year in the "before" part // of a filename, i.e. 2008 for "anything (2008) S01E01.mkv" matcher = pattern.matcher(mEpisodes.get(0).getBefore()); // Safe to use the 0-th element for the "before" part regardless of mEpisodes.size() if (matcher.find()) return MizLib.getInteger(matcher.group(1)); // Check if there's a release year in the "after" part // of a filename, i.e. 2008 for "anything S01E01 (2008).mkv" matcher = pattern.matcher(mEpisodes.get(mEpisodes.size() - 1).getAfter()); // Use the last element to check the "after" part if (matcher.find()) return MizLib.getInteger(matcher.group(1)); return -1; } public boolean hasReleaseYear() { return getReleaseYear() >= 0; } public ArrayList<Episode> decryptEpisodes(String filename) { // Remove known tags that can mess up the decryption stuff filename = filename.replaceAll("(?i)(?:(m?[-]?\\d{3,4}[ip])|[hx]264|\\d{3,4}mb)", ""); // i.e. m480p, 720p, 1080i, h264, x264, 700mb ArrayList<Episode> episodes = new ArrayList<Episode>(); // S##E## Pattern pattern = Pattern.compile("(.*?)[s](\\d{1,4})[ ._-]*[e](\\d{1,3})", Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(filename); if (matcher.find()) { int count = 1; // We already have one match while (matcher.find()) count++; // Count any additional matches if (count > 1) { // We're dealing with the S##E## S##E## format // Reset the Matcher so we can find all the matches again matcher.reset(); // Go through all matches and add each one while (matcher.find()) { String before = matcher.group(1); int season = MizLib.getInteger(matcher.group(2)); int episode = MizLib.getInteger(matcher.group(3)); String after = filename.substring(matcher.end(), filename.length()); episodes.add(new Episode(season, episode, before, after)); } } else { // We're dealing with a single instance of S##E## or a multi-episode format (i.e. S##E##E##E##) pattern = Pattern.compile("(.*?)[s](\\d{1,4})[ ._-]*[e](\\d{1,3}(?:[-_ex]\\d{1,3})*)(.*)", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(filename); // Go through all matches and add each one while (matcher.find()) { String before = matcher.group(1); String after = matcher.group(4); int season = MizLib.getInteger(matcher.group(2)); // Pre-compiled Pattern to speed up to the splitting process Pattern splitPattern = Pattern.compile("[-_ex]", Pattern.CASE_INSENSITIVE); for (String episode : splitPattern.split(matcher.group(3))) episodes.add(new Episode(season, MizLib.getInteger(episode), before, after)); } } } if (episodes.size() > 0) return episodes; // ##E## (has to begin with it) pattern = Pattern.compile("^(\\d{1,4})[ ._-]*[e](\\d{1,3})", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(filename); if (matcher.find()) { int season = MizLib.getInteger(matcher.group(1)); int episode = MizLib.getInteger(matcher.group(2)); String after = filename.substring(matcher.end(), filename.length()); episodes.add(new Episode(season, episode, "", after)); } if (episodes.size() > 0) return episodes; // season ## episode ## pattern = Pattern.compile("(.*?)(?:season|staffel|series)[ ._-]*(\\d{1,4})[ ._-]*episode[ ._-]*(\\d{1,3})(.*)", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(filename); if (matcher.find()) { String before = matcher.group(1); int season = MizLib.getInteger(matcher.group(2)); int episode = MizLib.getInteger(matcher.group(3)); String after = filename.substring(matcher.end(), filename.length()); episodes.add(new Episode(season, episode, before, after)); } if (episodes.size() > 0) return episodes; // ep##, episode## pattern = Pattern.compile("(.*?)[e][p](?:[i][s][o][d][e])?[ ._-]*(\\d{1,3})", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(filename); if (matcher.find()) { int count = 1; // We already have one match while (matcher.find()) count++; // Count any additional matches if (count > 1) { // We're dealing with the ep## ep## format // Reset the Matcher so we can find all matches again matcher.reset(); int season = 1; // Assumed since there's no season information with this naming convention // Go through all matches and add each one while (matcher.find()) { String before = matcher.group(1); int episode = MizLib.getInteger(matcher.group(2)); String after = filename.substring(matcher.end(), filename.length()); episodes.add(new Episode(season, episode, before, after)); } } else { // We're dealing with a single instance of ep## or a multi-episode format (i.e. ep##x##e##) pattern = Pattern.compile("(.*?)[e][p](?:[i][s][o][d][e])?[ ._-]*(\\d{1,3}(?:[-_ex]\\d{1,3})*)(.*)", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(filename); int season = 1; // Assumed since there's no season information with this naming convention // Go through all matches and add each one while (matcher.find()) { String before = matcher.group(1); String after = matcher.group(3); // Pre-compiled Pattern to speed up to the splitting process Pattern splitPattern = Pattern.compile("[-_ex]", Pattern.CASE_INSENSITIVE); for (String episode : splitPattern.split(matcher.group(2))) episodes.add(new Episode(season, MizLib.getInteger(episode), before, after)); } } } if (episodes.size() > 0) return episodes; // part##, pt## pattern = Pattern.compile("(.*?)[p](?:[a][r])?[t][ ._-]*(\\d{1,3})", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(filename); if (matcher.find()) { int count = 1; // We already have one match while (matcher.find()) count++; // Count any additional matches if (count > 1) { // We're dealing with the pt## part## format // Reset the Matcher so we can find all matches again matcher.reset(); int season = 1; // Assumed since there's no season information with this naming convention // Go through all matches and add each one while (matcher.find()) { String before = matcher.group(1); int episode = MizLib.getInteger(matcher.group(2)); String after = filename.substring(matcher.end(), filename.length()); episodes.add(new Episode(season, episode, before, after)); } } else { // We're dealing with a single instance of part## or a multi-episode format (i.e. pt##x##e##) pattern = Pattern.compile("(.*?)[p](?:[a][r])?[t][ ._-]*(\\d{1,3}(?:[-_ex]\\d{1,3})*)(.*)", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(filename); int season = 1; // Assumed since there's no season information with this naming convention // Go through all matches and add each one while (matcher.find()) { String before = matcher.group(1); String after = matcher.group(3); // Pre-compiled Pattern to speed up to the splitting process Pattern splitPattern = Pattern.compile("[-_ex]", Pattern.CASE_INSENSITIVE); for (String episode : splitPattern.split(matcher.group(2))) episodes.add(new Episode(season, MizLib.getInteger(episode), before, after)); } } } if (episodes.size() > 0) return episodes; // ##x## pattern = Pattern.compile("(.*?)(\\d{1,4})[ ._-]*[x](\\d{1,3})", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(filename); if (matcher.find()) { int count = 1; // We already have one match while (matcher.find()) count++; // Count any additional matches if (count > 1) { // We're dealing with the ##x## ##x## format // Reset the Matcher so we can find all matches again matcher.reset(); // Go through all matches and add each one while (matcher.find()) { String before = matcher.group(1); int season = MizLib.getInteger(matcher.group(2)); int episode = MizLib.getInteger(matcher.group(3)); String after = filename.substring(matcher.end(), filename.length()); episodes.add(new Episode(season, episode, before, after)); } } else { // We're dealing with a single instance of ##x## or a multi-episode format (i.e. ##x##x##e##) pattern = Pattern.compile("(.*?)(\\d{1,4})[ ._-]*[x](\\d{1,3}(?:[-_ex]\\d{1,3})*)(.*)", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(filename); // Go through all matches and add each one while (matcher.find()) { String before = matcher.group(1); String after = matcher.group(4); int season = MizLib.getInteger(matcher.group(2)); // Pre-compiled Pattern to speed up to the splitting process Pattern splitPattern = Pattern.compile("[-_ex]", Pattern.CASE_INSENSITIVE); for (String episode : splitPattern.split(matcher.group(3))) episodes.add(new Episode(season, MizLib.getInteger(episode), before, after)); } } } if (episodes.size() > 0) return episodes; // ### [3-7] pattern = Pattern.compile("(^.*?)((\\d){3,7})(.*?)$", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(filename); if (matcher.find()) { // We only want a single set of results here int season = 0, episode = 0; String before = matcher.group(1); String group = matcher.group(2); String after = matcher.group(3); switch (group.length()) { case 3: // ### (season [1], episode [2]) season = MizLib.getInteger(group.substring(0, 1)); episode = MizLib.getInteger(group.substring(1, 3)); break; case 4: // #### (season [2], episode [2]) season = MizLib.getInteger(group.substring(0, 2)); episode = MizLib.getInteger(group.substring(2, 4)); break; case 5: // ##### (season [2], episode [3]) season = MizLib.getInteger(group.substring(0, 2)); episode = MizLib.getInteger(group.substring(2, 5)); break; case 6: // ###### (season [3], episode [3]) season = MizLib.getInteger(group.substring(0, 3)); episode = MizLib.getInteger(group.substring(3, 6)); break; case 7: // ####### (season [4], episode [3]) season = MizLib.getInteger(group.substring(0, 4)); episode = MizLib.getInteger(group.substring(4, 7)); break; } episodes.add(new Episode(season, episode, before, after)); } if (episodes.size() > 0) return episodes; // ## [1-3] episode information only (has to start with this) pattern = Pattern.compile("^(\\d{1,3})(.*?)$", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(filename); if (matcher.find()) { // We only want a single set of results here int season = 1; // Assumed since there's no season information int episode = MizLib.getInteger(matcher.group(1)); String after = matcher.group(2); episodes.add(new Episode(season, episode, "", after)); // No "before" string } if (episodes.size() > 0) return episodes; // ## [1-2] episode information only (anywhere in the string) pattern = Pattern.compile("(.*?)(\\d{1,2})(.*?)$", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(filename); if (matcher.find()) { // We only want a single set of results here String before = matcher.group(1); int season = 1; // Assumed since there's no season information int episode = MizLib.getInteger(matcher.group(2)); String after = matcher.group(3); episodes.add(new Episode(season, episode, before, after)); } if (episodes.size() > 0) return episodes; return episodes; } }