/// <summary> /// Extracts season and episode number by using some regexes from config file /// </summary> /// <param name="ie">InfoEntry which should be processed</param> public static void ExtractSeasonAndEpisode(InfoEntry ie) { string[] patterns = Helper.ReadProperties(Config.EpIdentifier); for (int i = 0; i < patterns.Length; i++) { patterns[i] = RegexConverter.toRegex(patterns[i]); } ExtractSeasonAndEpisode(ie, patterns); }
public CollidingFiles(InfoEntry ie1, InfoEntry ie2) { InitializeComponent(); this.ie1 = ie1; this.ie2 = ie2; label2.Text = ie1.FilePath.Path + Path.DirectorySeparatorChar + ie1.Filename; label4.Text = ie1.GetFormattedFullDestination(); if (!ie2.ProcessingRequested) { btnSkipSecond.Enabled = false; } }
//we should do the other initializations here too, because they won't update when the user changes the configuration if they are initialized in the constructor private void reset() { string[] tags = Helper.ReadProperties(Config.Tags); List <string> MovieRegexes = new List <string>(); foreach (string s in tags) { MovieRegexes.Add("[^A-Za-z0-9]+" + s); } MovieTagPatterns = MovieRegexes.ToArray(); string[] blacklist = Helper.ReadProperties(Config.PathBlacklist); pathBlacklist = String.Join("|", blacklist); blacklist = Helper.ReadProperties(Config.FilenameBlacklist); filenameBlacklist = String.Join("|", blacklist); MovieNameFromDirectory = false; name = null; ie = null; part = -1; sequelNumber = -1; multifile = false; filenameBlacklisted = false; }
private void reset() { seriesNameFromDirectory = false; name = null; ie = null; filenameBlacklisted = false; }
/// <summary> /// Updatess list view and do lots of other connected stuff with it /// </summary> /// <param name="clear">if true, list is cleared first and unconnected subtitle files are scheduled to be renamed</param> /// <param name="KeepShowName">if set, show name isn't altered</param> public static void UpdateList(bool clear, BackgroundWorker worker, DoWorkEventArgs e) { InfoEntryManager infoManager = InfoEntryManager.Instance; // Clear list if desired, remove deleted files otherwise if (clear) { infoManager.Clear(); } else { infoManager.RemoveMissingFileEntries(); } // read path from config && remove tailing slashes string path = Helper.ReadProperty(Config.LastDirectory); path = path.TrimEnd(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }); Logger.Instance.LogMessage("Opening folder " + path, LogLevel.DEBUG); bool CreateDirectoryStructure = Helper.ReadBool(Config.CreateDirectoryStructure); bool UseSeasonSubdirs = Helper.ReadBool(Config.UseSeasonSubDir); if (Directory.Exists(path)) { //scan for new files List<string> extensions = new List<string>(Helper.ReadProperties(Config.Extensions)); extensions.AddRange(Helper.ReadProperties(Config.SubtitleExtensions)); if (extensions == null) { Logger.Instance.LogMessage("No File Extensions found!", LogLevel.WARNING); return; } //convert all extensions to lowercase for (int i = 0; i < extensions.Count; i++) { extensions[i] = extensions[i].ToLower(); } //read all files with matching extension List<FileSystemInfo> Files = new List<FileSystemInfo>(); int count = 0; foreach (string ex in extensions) { Files.AddRange(Helper.GetAllFilesRecursively(path, "*." + ex, ref count,worker)); } if (worker.CancellationPending) { e.Cancel = true; Logger.Instance.LogMessage("Cancelled opening folder.", LogLevel.INFO); return; } if (Form1.Instance.InvokeRequired) { Form1.Instance.Invoke(new EventHandler(delegate { Form1.Instance.lblFileListingProgress.Visible = false; Form1.Instance.progressBar1.Visible = true; })); } else { Form1.Instance.lblFileListingProgress.Visible = false; Form1.Instance.progressBar1.Visible = true; } //some declarations already for speed string[] patterns = Helper.ReadProperties(Config.EpIdentifier); for (int i = 0; i < patterns.Length; i++) { patterns[i] = RegexConverter.toRegex(patterns[i]); } int DirectorySeason = -1; InfoEntry ie = null; bool contains = false; DateTime dt; string currentpath = ""; string MovieIndicator = String.Join("|", Helper.ReadProperties(Config.MovieIndicator)); //Form1.Instance.progressBar1.Maximum = Files.Count; for(int f=0;f<Files.Count;f++){ if (worker.CancellationPending) { e.Cancel = true; Logger.Instance.LogMessage("Cancelled opening folder.", LogLevel.INFO); return; } //Form1.Instance.progressBar1.Value=f; worker.ReportProgress((int) ((double)f/((double)Files.Count)*100)); FileSystemInfo file=Files[f]; //showname and season recognized from path DirectorySeason = -1; //Check if there is already an entry on this file, and if not, create one ie = null; currentpath = Path.GetDirectoryName(file.FullName); foreach (InfoEntry i in InfoEntryManager.Instance) { if (i.Filename == file.Name && i.FilePath.Path == currentpath) { ie = i; break; } } if (ie == null) { ie = new InfoEntry(); } //test for movie path so we can skip all series recognition code if (Regex.Match(currentpath, MovieIndicator).Success) { ie.Movie = true; } //Set basic values, by setting those values destination directory and filename will be generated automagically ie.FilePath.Path = currentpath; ie.Filename = file.Name; ie.Extension = Path.GetExtension(file.FullName).ToLower().Replace(".", ""); if(!ie.Movie) { //Get season number and showname from directory DirectorySeason = ExtractSeasonFromDirectory(Path.GetDirectoryName(file.FullName)); dt = DateTime.Now; //try to recognize season and episode from filename /////////////////////////////////////////////////// ExtractSeasonAndEpisode(ie, patterns); /////////////////////////////////////////////////// //Need to do this for filenames which only contain episode numbers (But might be moved in a season dir already) //if season number couldn't be extracted, try to get it from folder if (ie.Season <= 0 && DirectorySeason != -1) { ie.Season = DirectorySeason; } //if season recognized from directory name doesn't match season recognized from filename, the file might be located in a wrong directory if (DirectorySeason != -1 && ie.Season != DirectorySeason) { Logger.Instance.LogMessage("File seems to be located inconsistently: " + ie.Filename + " was recognized as season " + ie.Season + ", but folder name indicates that it should be season " + DirectorySeason.ToString(), LogLevel.WARNING); } } //if nothing could be recognized, assume that this is a movie if ((ie.Season < 1 && ie.Episode < 1) || ie.Movie) { ie.RemoveVideoTags(); } //if not added yet, add it if (!contains) { InfoEntryManager.Instance.Add(ie); } } //SelectSimilarFilesForProcessing(path,Helper.ReadProperties(Config.LastTitles)[0]); SelectRecognizedFilesForProcessing(); } //Recreate subtitle names so they can adjust to the video files they belong to foreach (InfoEntry ie in InfoEntryManager.Instance) { if (ie.IsSubtitle) { ie.CreateNewName(); } } Logger.Instance.LogMessage("Found " + InfoEntryManager.Instance.Count + " Files", LogLevel.INFO); if (Helper.ReadBool(Config.FindMissingEpisodes)) { FindMissingEpisodes(); } }
/// <summary> /// Extracts season and episode number by using some regexes specified in patterns /// </summary> /// <param name="ie">InfoEntry which should be processed</param> /// <param name="patterns">Patterns to be used for recognition, supply these for speed reasons?</param> public static void ExtractSeasonAndEpisode(InfoEntry ie, string[] patterns) { string strSeason = ""; string strEpisode = ""; Match m = null; int episode = -1; int season = -1; Logger.Instance.LogMessage("Extracting season and episode from " + ie.FilePath + Path.DirectorySeparatorChar+ie.Filename, LogLevel.DEBUG); //[Tags] and (CRCXXXXX) are removed for recognition, since they might produce false positives string cleanedname = Regex.Replace(ie.Filename, "\\[.*?\\]|\\(.{8}\\)", ""); foreach (string pattern in patterns) { //Try to match. If it works, get the season and the episode from the match m = Regex.Match(cleanedname, pattern, RegexOptions.IgnoreCase | RegexOptions.RightToLeft); if (m.Success) { Logger.Instance.LogMessage("This pattern produced a match: " + pattern, LogLevel.DEBUG); strSeason = ""; strEpisode = ""; //ignore numbers like 2063, chance is higher they're a year than a valid season/ep combination if (Int32.Parse(m.Groups["Season"].Value + m.Groups["Episode"].Value) > 2000 && (m.Groups["Season"].Value + m.Groups["Episode"].Value).StartsWith("20")) { continue; } try { strSeason = Int32.Parse(m.Groups["Season"].Value).ToString(); } catch (FormatException) { } try { strEpisode = Int32.Parse(m.Groups["Episode"].Value).ToString(); } catch (FormatException) { } //Fix for .0216. notation for example, 4 numbers should always be recognized as %S%S%E%E (IF THEY ARENT A YEAR!) if (strEpisode.Length == 3 && strSeason.Length == 1) { strSeason += strEpisode[0]; strEpisode = strEpisode.Substring(1); if (strSeason[0] == '0') { strSeason = strSeason.Substring(1); } } try { episode = Int32.Parse(strEpisode); } catch { Logger.Instance.LogMessage("Cannot parse found episode: " + strEpisode, LogLevel.DEBUG); } try { season = Int32.Parse(strSeason); } catch { Logger.Instance.LogMessage("Cannot parse found season: " + strSeason, LogLevel.DEBUG); } if ((ie.Filename.ToLower().Contains("720p") && season == 7 && episode == 20) | (ie.Filename.ToLower().Contains("1080p") && season == 10 && episode == 80)) { season = -1; episode = -1; continue; } ie.Season = season; ie.Episode = episode; return; } } }
//NOTE: Need a feature to detect if the sequel name is already contained within the filename but was found somewhere else(eg directory) and added to the end again, e.g. "Hackers 2 Operation Takedown 2" public string ExtractMovieName(InfoEntry ie) { reset(); this.ie = ie; name = ie.FilePath.Name; folders=new List<string>(Filepath.extractFoldernamesFromPath(ie.FilePath.Path)); //folderlevels are used to see which directory contains the video name, so we can set ie.extractednamelevel for path creation List<int> folderlevels = new List<int>(); int origcount=folders.Count; if (Regex.IsMatch(name, filenameBlacklist, RegexOptions.IgnoreCase)) { filenameBlacklisted = true; //must be atleast 1 then ie.ExtractedNameLevel = 1; } //Remove all illegal paths int j = 0; for(int i=0; i<folders.Count;i++){ if (Regex.IsMatch(folders[i], pathBlacklist, RegexOptions.IgnoreCase)) { folders.RemoveAt(i); i--; } else { folderlevels.Add(origcount-j); } j++; } if(filenameBlacklisted&&folders.Count==0){ return "Not Recognized"; } if (!filenameBlacklisted) { folders.Add(ie.FilePath.Name); folderlevels.Add(0); } //The idea here is to test if the current name might be a better name instead of the previous one //We go upwards and try to find part and sequel numbers, as well as movie name. Those might be from different folders, so we need to check all legit ones //Clean first name (sequel and part should still be -1 name = ""; for (int i = folders.Count - 1; i >= 0; i--) { string testname = folders[i]; int testpart = -1; int testsequel = -1; //Test for sample if (testname.ToLower().Contains("sample")) { name = "Sample"; return name; } testname = NameCleanup.RemoveReleaseGroupTag(testname); int firsttag = testname.Length; //remove tags and store the first occurence of a tag //since we may miss a few tags, the string after the first occurence of a tag is removed later (not now since it may //contain additional information). Tags need to be removed before part and sequel detection, to avoid detecting things like 720p foreach (string s in MovieTagPatterns) { Match m = Regex.Match(testname, s, RegexOptions.IgnoreCase); if (m.Success) { if (m.Index < firsttag) { firsttag = m.Index; } testname = testname.Substring(0, m.Index)+testname.Substring(m.Index+m.Length,testname.Length-(m.Index+m.Length)); } } testpart = ExtractPartNumber(ref testname, ref firsttag); if (testpart != -1) { ie.IsMultiFileMovie = true; } testsequel = ExtractSequelNumber(ref testname,ref firsttag); //now after recognition of part and sequel numbers, remove the rest too testname = testname.Substring(0, firsttag); testname = NameCleanup.Postprocessing(testname); //Some counterchecks against previous result here if (testpart != -1 && part == -1) { part = testpart; ie.ExtractedNameLevel = folderlevels[i]; } if (testsequel != -1 && sequelNumber == -1) sequelNumber = testsequel; if (name==""||Helper.InitialsMatch(testname, name)) name = testname; } if (sequelNumber != -1) { name += " " + sequelNumber; } if (part != -1) { name += " CD" + part; } return name; }
//we should do the other initializations here too, because they won't update when the user changes the configuration if they are initialized in the constructor private void reset() { string[] tags = Helper.ReadProperties(Config.Tags); List<string> MovieRegexes = new List<string>(); foreach (string s in tags) { MovieRegexes.Add("[^A-Za-z0-9]+" + s); } MovieTagPatterns = MovieRegexes.ToArray(); string[] blacklist = Helper.ReadProperties(Config.PathBlacklist); pathBlacklist = String.Join("|", blacklist); blacklist = Helper.ReadProperties(Config.FilenameBlacklist); filenameBlacklist = String.Join("|", blacklist); MovieNameFromDirectory = false; name = null; ie = null; part = -1; sequelNumber = -1; multifile = false; filenameBlacklisted = false; }
/// <summary> /// Gets video file matching to subtitle /// </summary> /// <param name="ieSubtitle">InfoEntry of a subtitle to find matching video file for</param> /// <returns>Matching video file</returns> public InfoEntry GetVideo(InfoEntry ieSubtitle) { List<string> vidext = new List<string>(Helper.ReadProperties(Config.Extensions)); foreach (InfoEntry ie in this.episodes) { if (Path.GetFileNameWithoutExtension(ieSubtitle.Filename) == Path.GetFileNameWithoutExtension(ie.Filename)) { if (vidext.Contains(ie.Extension)) { return ie; } } } return null; }
internal void Add(InfoEntry ie) { this.episodes.Add(ie); }
public void Remove(InfoEntry ie) { this.episodes.Remove(ie); }
/// <summary> /// figures out if 2 infoentry target locations collide /// </summary> /// <param name="ie1"></param> /// <param name="ie2"></param> /// <returns></returns> public bool IsSameTarget(InfoEntry ie1, InfoEntry ie2) { if (ie1 == ie2) return false; string name1, name2, dest1, dest2; if (ie1.Destination == "") { dest1 = ie1.FilePath.Path; } else { dest1 = ie1.Destination; if (!ie1.ProcessingRequested) return false; } if (ie2.Destination == "") { dest2 = ie2.FilePath.Path; } else { dest2 = ie2.Destination; if (!ie2.ProcessingRequested) return false; } if (ie1.NewFilename == "") { name1 = ie1.Filename; } else { name1 = ie1.NewFilename; if (!ie1.ProcessingRequested) return false; } if (ie2.NewFilename == "") { name2 = ie2.Filename; } else { name2 = ie2.NewFilename; if (!ie2.ProcessingRequested) return false; } return name1 == name2 && dest1 == dest2; }
public int IndexOf(InfoEntry ie) { for (int i = 0; i < episodes.Count; i++) { if (episodes[i] == ie) { return i; } } return -1; }
public string ExtractSeriesName(InfoEntry ie) { reset(); this.ie = ie; // Read plain filename string filename = System.IO.Path.GetFileNameWithoutExtension(ie.Filename); filename = NameCleanup.RemoveReleaseGroupTag(filename); folders = Filepath.extractFoldernamesFromPath(ie.FilePath.Path); if (ie.InSeasonFolder && folders.Length > 2) { if (!Regex.IsMatch(folders[folders.Length - 2], pathBlacklist, RegexOptions.IgnoreCase)) { return folders[folders.Length - 2]; } } extractNameFromSeasonsFolder(); extractNameFromString(filename); if (folders.Length != 0) { extractNameFromString(folders[folders.Length - 1]); } fallbackFolderNames(); name=NameCleanup.Postprocessing(name); if (name == null) return ""; return name; }
//NOTE: Need a feature to detect if the sequel name is already contained within the filename but was found somewhere else(eg directory) and added to the end again, e.g. "Hackers 2 Operation Takedown 2" public string ExtractMovieName(InfoEntry ie) { reset(); this.ie = ie; name = ie.FilePath.Name; folders = new List <string>(Filepath.extractFoldernamesFromPath(ie.FilePath.Path)); //folderlevels are used to see which directory contains the video name, so we can set ie.extractednamelevel for path creation List <int> folderlevels = new List <int>(); int origcount = folders.Count; if (Regex.IsMatch(name, filenameBlacklist, RegexOptions.IgnoreCase)) { filenameBlacklisted = true; //must be atleast 1 then ie.ExtractedNameLevel = 1; } //Remove all illegal paths int j = 0; for (int i = 0; i < folders.Count; i++) { if (Regex.IsMatch(folders[i], pathBlacklist, RegexOptions.IgnoreCase)) { folders.RemoveAt(i); i--; } else { folderlevels.Add(origcount - j); } j++; } if (filenameBlacklisted && folders.Count == 0) { return("Not Recognized"); } if (!filenameBlacklisted) { folders.Add(ie.FilePath.Name); folderlevels.Add(0); } //The idea here is to test if the current name might be a better name instead of the previous one //We go upwards and try to find part and sequel numbers, as well as movie name. Those might be from different folders, so we need to check all legit ones //Clean first name (sequel and part should still be -1 name = ""; for (int i = folders.Count - 1; i >= 0; i--) { string testname = folders[i]; int testpart = -1; int testsequel = -1; //Test for sample if (testname.ToLower().Contains("sample")) { name = "Sample"; return(name); } testname = NameCleanup.RemoveReleaseGroupTag(testname); int firsttag = testname.Length; //remove tags and store the first occurence of a tag //since we may miss a few tags, the string after the first occurence of a tag is removed later (not now since it may //contain additional information). Tags need to be removed before part and sequel detection, to avoid detecting things like 720p foreach (string s in MovieTagPatterns) { Match m = Regex.Match(testname, s, RegexOptions.IgnoreCase); if (m.Success) { if (m.Index < firsttag) { firsttag = m.Index; } testname = testname.Substring(0, m.Index) + testname.Substring(m.Index + m.Length, testname.Length - (m.Index + m.Length)); } } testpart = ExtractPartNumber(ref testname, ref firsttag); if (testpart != -1) { ie.IsMultiFileMovie = true; } testsequel = ExtractSequelNumber(ref testname, ref firsttag); //now after recognition of part and sequel numbers, remove the rest too testname = testname.Substring(0, firsttag); testname = NameCleanup.Postprocessing(testname); //Some counterchecks against previous result here if (testpart != -1 && part == -1) { part = testpart; ie.ExtractedNameLevel = folderlevels[i]; } if (testsequel != -1 && sequelNumber == -1) { sequelNumber = testsequel; } if (name == "" || Helper.InitialsMatch(testname, name)) { name = testname; } } if (sequelNumber != -1) { name += " " + sequelNumber; } if (part != -1) { name += " CD" + part; } return(name); }
/// <summary> /// This function generates a new filename from the Target Pattern, episode, season, title, showname,... values /// </summary> public void CreateNewName() { if ((Movie && Showname == "") || (!Movie && !isSubtitle && nameOfEpisode == "")) { NewFilename = ""; return; } else { //Note: Subtitle destination path is set here too if (isSubtitle) { if (nameOfEpisode == "" && Season > -1 && Episode > -1) { InfoEntry videoEntry = InfoEntryManager.Instance.GetMatchingVideo(Showname, Season, Episode); if (videoEntry != null) { string nfn, dst; if (videoEntry.NewFilename == "") { nfn = Path.GetFileNameWithoutExtension(videoEntry.Filename); } else { nfn = Path.GetFileNameWithoutExtension(videoEntry.NewFilename); } nfn += "." + Extension; //Move to Video file dst = videoEntry.Destination; //Don't do this if name fits already if (nfn == Filename) { nfn = ""; } //Don't do this if path fits already if (dst == FilePath.Path) { dst = ""; } NewFilename = nfn; Destination = dst; return; } else { return; } } else { return; } } //Target Filename format string tmpname; //Those 3 strings need case/Umlaut processing string epname = this.nameOfEpisode; string seriesname = nameOfSeries; string extension = Extension; if (!isMovie) { tmpname = Helper.ReadProperty(Config.TargetPattern); tmpname = tmpname.Replace("%e", episode.ToString()); tmpname = tmpname.Replace("%E", episode.ToString("00")); tmpname = tmpname.Replace("%s", season.ToString()); tmpname = tmpname.Replace("%S", season.ToString("00")); adjustSpelling(ref epname, false); } else { tmpname = "%T"; } adjustSpelling(ref seriesname, false); adjustSpelling(ref extension, true); //Now that series title, episode title and extension are properly processed, add them to the filename //Remove extension from target filename (if existant) and add properly cased one tmpname = Regex.Replace(tmpname, "\\." + extension, "", RegexOptions.IgnoreCase); tmpname += "." + extension; tmpname = tmpname.Replace("%T", seriesname); tmpname = tmpname.Replace("%N", epname); //string replace function List <string> replace = new List <string>(Helper.ReadProperties(Config.Replace)); List <string> from = new List <string>(); List <string> to = new List <string>(); foreach (string s in replace) { if (!s.StartsWith(Settings.Instance.Comment)) { string[] replacement = s.Split(new string[] { "->" }, StringSplitOptions.RemoveEmptyEntries); if (replacement != null && replacement.Length == 2) { tmpname = Regex.Replace(tmpname, replacement[0], replacement[1], RegexOptions.IgnoreCase); } } } //set new filename if renaming process is required if (Filename == tmpname) { NewFilename = ""; } else { NewFilename = tmpname; } } }
public InfoEntry GetCollidingInfoEntry(InfoEntry ie) { foreach(InfoEntry ie2 in this){ if(IsSameTarget(ie,ie2)){ return ie2; } } return null; }