Last active
August 29, 2015 14:19
-
-
Save parabola949/a159eb9411a1858704c4 to your computer and use it in GitHub Desktop.
Auto unpack and move torrents for TV and Movies
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Text; | |
public class Ini | |
{ | |
Dictionary<string, Dictionary<string, string>> ini = new Dictionary<string, Dictionary<string, string>>(StringComparer.InvariantCultureIgnoreCase); | |
string file; | |
/// <summary> | |
/// Initialize an INI file | |
/// Load it if it exists | |
/// </summary> | |
/// <param name="file">Full path where the INI file has to be read from or written to</param> | |
public Ini(string file) | |
{ | |
this.file = file; | |
if (!File.Exists(file)) | |
return; | |
Load(); | |
} | |
/// <summary> | |
/// Load the INI file content | |
/// </summary> | |
public void Load() | |
{ | |
var txt = File.ReadAllText(file); | |
Dictionary<string, string> currentSection = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase); | |
ini[""] = currentSection; | |
foreach (var l in txt.Split(new[] { "\n" }, StringSplitOptions.RemoveEmptyEntries) | |
.Select((t, i) => new | |
{ | |
idx = i, | |
text = t.Trim() | |
})) | |
// .Where(t => !string.IsNullOrWhiteSpace(t) && !t.StartsWith(";"))) | |
{ | |
var line = l.text; | |
if (line.StartsWith(";") || string.IsNullOrWhiteSpace(line)) | |
{ | |
currentSection.Add(";" + l.idx.ToString(), line); | |
continue; | |
} | |
if (line.StartsWith("[") && line.EndsWith("]")) | |
{ | |
currentSection = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase); | |
ini[line.Substring(1, line.Length - 2)] = currentSection; | |
continue; | |
} | |
var idx = line.IndexOf("="); | |
if (idx == -1) | |
currentSection[line] = ""; | |
else | |
currentSection[line.Substring(0, idx)] = line.Substring(idx + 1); | |
} | |
} | |
/// <summary> | |
/// Get a parameter value at the root level | |
/// </summary> | |
/// <param name="key">parameter key</param> | |
/// <returns></returns> | |
public string GetValue(string key) | |
{ | |
return GetValue(key, "", ""); | |
} | |
/// <summary> | |
/// Get a parameter value in the section | |
/// </summary> | |
/// <param name="key">parameter key</param> | |
/// <param name="section">section</param> | |
/// <returns></returns> | |
public string GetValue(string key, string section) | |
{ | |
return GetValue(key, section, ""); | |
} | |
/// <summary> | |
/// Returns a parameter value in the section, with a default value if not found | |
/// </summary> | |
/// <param name="key">parameter key</param> | |
/// <param name="section">section</param> | |
/// <param name="default">default value</param> | |
/// <returns></returns> | |
public string GetValue(string key, string section, string @default) | |
{ | |
if (!ini.ContainsKey(section)) | |
return @default; | |
if (!ini[section].ContainsKey(key)) | |
return @default; | |
return ini[section][key]; | |
} | |
/// <summary> | |
/// Save the INI file | |
/// </summary> | |
public void Save() | |
{ | |
var sb = new StringBuilder(); | |
foreach (var section in ini) | |
{ | |
if (section.Key != "") | |
{ | |
sb.AppendFormat("[{0}]", section.Key); | |
sb.AppendLine(); | |
} | |
foreach (var keyValue in section.Value) | |
{ | |
if (keyValue.Key.StartsWith(";")) | |
{ | |
sb.Append(keyValue.Value); | |
sb.AppendLine(); | |
} | |
else | |
{ | |
sb.AppendFormat("{0}={1}", keyValue.Key, keyValue.Value); | |
sb.AppendLine(); | |
} | |
} | |
if (!endWithCRLF(sb)) | |
sb.AppendLine(); | |
} | |
File.WriteAllText(file, sb.ToString()); | |
} | |
bool endWithCRLF(StringBuilder sb) | |
{ | |
if (sb.Length < 4) | |
return sb[sb.Length - 2] == '\r' && | |
sb[sb.Length - 1] == '\n'; | |
else | |
return sb[sb.Length - 4] == '\r' && | |
sb[sb.Length - 3] == '\n' && | |
sb[sb.Length - 2] == '\r' && | |
sb[sb.Length - 1] == '\n'; | |
} | |
/// <summary> | |
/// Write a parameter value at the root level | |
/// </summary> | |
/// <param name="key">parameter key</param> | |
/// <param name="value">parameter value</param> | |
public void WriteValue(string key, string value) | |
{ | |
WriteValue(key, "", value); | |
} | |
/// <summary> | |
/// Write a parameter value in a section | |
/// </summary> | |
/// <param name="key">parameter key</param> | |
/// <param name="section">section</param> | |
/// <param name="value">parameter value</param> | |
public void WriteValue(string key, string section, string value) | |
{ | |
Dictionary<string, string> currentSection; | |
if (!ini.ContainsKey(section)) | |
{ | |
currentSection = new Dictionary<string, string>(); | |
ini.Add(section, currentSection); | |
} | |
else | |
currentSection = ini[section]; | |
currentSection[key] = value; | |
} | |
/// <summary> | |
/// Get all the keys names in a section | |
/// </summary> | |
/// <param name="section">section</param> | |
/// <returns></returns> | |
public string[] GetKeys(string section) | |
{ | |
if (!ini.ContainsKey(section)) | |
return new string[0]; | |
return ini[section].Keys.ToArray(); | |
} | |
/// <summary> | |
/// Get all the section names of the INI file | |
/// </summary> | |
/// <returns></returns> | |
public string[] GetSections() | |
{ | |
return ini.Keys.Where(t => t != "").ToArray(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Linq; | |
using System.Runtime.InteropServices; | |
using System.Text.RegularExpressions; | |
using System.Threading; | |
namespace UnpackTorrent | |
{ | |
class Program | |
{ | |
static string _movieDir, _tvDir, _torDir, _winRarPath, _logFilePath; | |
static string _vidQualRegex = @"(720p)|(1080p)|(DVDSCR)|(iNTERNAL)|(HDTV)"; | |
static string _tvRegex = @"(S\d{2}E\d{2})"; | |
static string _vidFileRegex = @"(mkv)|(avi)|(wmv)"; | |
static string _sampleFileRegex = @"(sample).*((mkv)|(avi)|(wmv))"; | |
static bool _includeYearForMovies; | |
//Import dlls for window hiding... | |
[DllImport("kernel32.dll")] | |
static extern IntPtr GetConsoleWindow(); | |
[DllImport("user32.dll")] | |
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); | |
const int SW_HIDE = 0; | |
const int SW_SHOW = 5; | |
//Example arg[0] D:\Torrents\Game.of.Thrones.S05E02.720p.HDTV.x264-IMMERSE | |
//Example arg[1] Game.of.Thrones.S05E02.720p.HDTV.x264-IMMERSE | |
static void Main(string[] args) | |
{ | |
//attempt to hide the console window | |
try | |
{ | |
ShowWindow(GetConsoleWindow(), SW_HIDE); | |
} | |
catch(Exception e) | |
{ | |
Log("Error hiding console window: {0}", e.Message); | |
} | |
//Load settings | |
var INI = new Ini("settings.ini"); | |
_movieDir = INI.GetValue("Movies","Directories"); | |
_tvDir = INI.GetValue("TV","Directories"); | |
_torDir = INI.GetValue("Torrents", "Directories"); | |
_includeYearForMovies = INI.GetValue("Include Year", "Other") == "True"; | |
_winRarPath = INI.GetValue("WinRAR Path", "Other"); | |
_logFilePath = INI.GetValue("Log File", "Directories", "unpack.log"); | |
Log("--------------------------------------------"); | |
Log("Beginning unpack / copy. Torrent: {0} Directory: {1}", args[1], args[0]); | |
var fileInRoot = false; | |
//Check that the item is in it's own folder | |
if (_torDir == (args[0])) | |
{ | |
if (Directory.GetFiles(_torDir).Any(x => x.Contains(args[1]))) | |
fileInRoot = true; | |
else | |
{ | |
Log("Torrent in root directory, and file name does not match torrent name. Cannot find. Exiting."); | |
Environment.Exit(2); | |
} | |
} | |
Log("File in root: {0}", fileInRoot); | |
var parseStr = fileInRoot ? args[1] : args[0].Substring(args[0].LastIndexOf("\\") + 1); | |
//Now check if it is a movie or tv show (not a game or software) | |
if (!Regex.IsMatch(parseStr, _vidQualRegex, RegexOptions.IgnoreCase)) | |
{ | |
//Does not appear to be a movie, skip this file. | |
Log("This torrent does not appear to be a video. Skipping.\nTorrent: {0}", args[1]); | |
Environment.Exit(1); | |
} | |
//Is it a TV Show or a Movie? | |
if (Regex.IsMatch(parseStr, _tvRegex, RegexOptions.IgnoreCase)) | |
{ | |
//TV Show | |
//Ugly bits, parsing out title, season, episode (though.. we don't REALLY need the episode, but whatever) | |
var s00e00 = Regex.Matches(parseStr, _tvRegex)[0].Value; | |
var title = parseStr; | |
title = title.Substring(0, title.IndexOf(s00e00)); | |
title = title.Replace('.', ' ').Trim(); | |
var season = s00e00.Substring(1, 2); | |
var episode = s00e00.Substring(4, 2); | |
if (season[0] == '0') | |
season = season.Substring(1); | |
//Check our directories now. | |
//Does the show have a directory? | |
var showRoot = _tvDir + "\\" + title; | |
if (!Directory.Exists(showRoot)) | |
Directory.CreateDirectory(showRoot); | |
//Does the season have a directory? | |
var seasonDir = showRoot + "\\Season " + season; | |
if (!Directory.Exists(seasonDir)) | |
Directory.CreateDirectory(seasonDir); | |
//Now then, do we have a video file, or rar? Essentially, are at least half the files rars (in case there is a single rar file and a video file + nfo file) | |
//Forgive me for this logic.... | |
if (!fileInRoot && IsRar(args[0])) | |
{ | |
//RAR it is! | |
foreach (var file in Directory.GetFiles(args[0]).Where(x => x.EndsWith(".rar"))) | |
{ | |
Log("Unpacking RAR files from {0} and moving to {1}", file, seasonDir); | |
//Unpack.. winrar.exe x <sourcerar> <destpath> | |
Process p = Process.Start(_winRarPath, String.Format("x -ibck -o+ \"{0}\" \"{1}\"", file, seasonDir)); | |
p.WaitForExit(); | |
} | |
//And we are done.. | |
Environment.Exit(0); | |
} | |
else | |
{ | |
//single video file | |
foreach (var file in Directory.GetFiles(args[0]).Where(x => Regex.IsMatch(x, _vidFileRegex, RegexOptions.IgnoreCase) & !Regex.IsMatch(x, _sampleFileRegex, RegexOptions.IgnoreCase) && (fileInRoot ? x.Contains(parseStr) : true))) | |
{ | |
Log("Copying file {0} to {1}", file, seasonDir); | |
File.Copy(file, seasonDir + file.Substring(file.LastIndexOf("\\")), true); | |
} | |
} | |
} | |
else | |
{ | |
//Looks like it's a movie. | |
var title = parseStr; | |
title = title.Substring(0, title.IndexOf(Regex.Match(title, _vidQualRegex).Value)); | |
//now we should have title.year | |
var year = title.Substring(title.Length - 5, 4); | |
title = title.Substring(0, title.IndexOf(year) - 1); | |
title = title.Replace("_", " ").Replace('.', ' ').Trim(); | |
if (_includeYearForMovies) | |
title += " (" + year + ")"; | |
//now to unpack | |
if (!fileInRoot && IsRar(args[0])) | |
{ | |
foreach (var file in Directory.GetFiles(args[0]).Where(x => x.EndsWith(".rar") && (fileInRoot ? x.Contains(parseStr) : true))) | |
{ | |
//Unpack.. winrar.exe x <sourcerar> <destpath> | |
Directory.CreateDirectory(_movieDir + "\\temp"); | |
Process p = Process.Start(_winRarPath, String.Format("x -ibck -o+ \"{0}\" \"{1}\"", file, _movieDir + "\\temp")); | |
p.WaitForExit(); | |
foreach (var video in Directory.GetFiles(_movieDir + "\\temp").Where(x => !x.EndsWith(".ini"))) //Stupid desktop.ini.... | |
{ | |
//ok, madness here. standard naming convention is title.year.quality.source.group | |
var extension = video.Substring(video.LastIndexOf('.')); | |
title += extension; | |
var moviePath = _movieDir + "\\" + title; | |
if (File.Exists(moviePath)) | |
{ | |
Log("Removing old file {0}", moviePath); | |
File.Delete(moviePath); | |
} | |
Log("Moving file {0} to {1}", video, moviePath); | |
File.Move(video, moviePath); | |
} | |
} | |
} | |
else | |
{ | |
int i = 0; //just in case of multipart | |
//it's a movie file, we just need to copy it over (not move... SEED BITCHES!) | |
foreach (var file in Directory.GetFiles(args[0]).Where(x => Regex.IsMatch(x, _vidFileRegex) && !Regex.IsMatch(x, _sampleFileRegex) && (fileInRoot ? x.Contains(parseStr) : true))) | |
{ | |
i++; | |
var extension = file.Substring(file.LastIndexOf('.')); | |
var path = _movieDir + "\\" + title + (i > 1 ? " Part (" + i + ")" : "") + extension; | |
Log("Copying {0} to {1}", file, path); | |
File.Copy(file, path, true); | |
} | |
} | |
} | |
} | |
private static bool IsRar(string path) | |
{ | |
return ((double)(Directory.GetFiles(path).Count(x => Regex.IsMatch(x, @"r\d{2}") || x.EndsWith("rar")))) / ((double)Directory.GetFiles(path).Count()) >= 0.5 & !Directory.GetFiles(path).Any(x => Regex.IsMatch(x, _vidFileRegex, RegexOptions.IgnoreCase) & !Regex.IsMatch(x, _sampleFileRegex, RegexOptions.IgnoreCase)); | |
} | |
private static void Log(string str, params object[] args) | |
{ | |
var logStr = args == null?str:String.Format(str,args); | |
Console.WriteLine(logStr); | |
using (var sw = new StreamWriter(_logFilePath, true)) | |
{ | |
sw.WriteLine(logStr); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[Directories] | |
Movies=D:\Google Drive\Media\Movies | |
TV=D:\Google Drive\Media\TV Shows | |
Torrents=D:\Torrents | |
;Log file path. This can be absolute or relative, and can be whatever extension you want. it is just plain text. | |
Log File=D:\Torrents\unpack.log | |
[Other] | |
;path to winrar | |
WinRAR Path=C:\Program Files\WinRAR\WinRAR.exe | |
;include year in movie file name | |
Include Year=True | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment