private static void ManageTracker(TorrentHandler H) { Console.Clear(); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Adding and removing trackers is not supported on MonoTorrent"); Console.ResetColor(); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("List of public trackers we know:"); if (TorrentHandler.PublicTrackers != null) { foreach (var T in TorrentHandler.PublicTrackers) { Console.WriteLine(T); } } else { Console.WriteLine("none (Download of list failed)"); } Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Trackers registered for this torrent"); foreach (var T in H.GetTracker()) { Console.WriteLine("{0,-30} {1}", T.Status, T.Uri); } Console.Write("Press any key to go back"); Console.ReadKey(); /* * bool cont = true; * while (cont) * { * Console.Clear(); * Console.SetCursorPosition(0, Console.WindowHeight - 1); * Console.Write("Enter a new tracker URL to add or nothing to go back"); * Console.SetCursorPosition(0, 0); * foreach (var T in H.GetTracker()) * { * Console.WriteLine(T); * } * var URL = Console.ReadLine(); * if (!string.IsNullOrEmpty(URL)) * { * try * { * H.AddTracker(new Uri(URL)); * } * catch * { * //NOOP * } * } * else * { * cont = false; * } * } * //*/ }
public static int Main(string[] args) { LaunchedFromCmd = (ParentProcessUtilities.GetParentProcessName().ToLower() == Environment.ExpandEnvironmentVariables("%COMSPEC%").ToLower()); #if DEBUG args = new string[] { //2018-04-18-raspbian-stretch-lite.zip "05E76C8B1795CE49E203BE4C39E378F7A97CBED5" }; #endif Console.WriteLine("Grabbing public trackers"); using (var CL = new HttpClient()) { CL.DefaultRequestHeaders.Add("User-Agent", "AyrA-QuickTorrent/1.0"); try { var Result = CL .GetAsync("https://cable.ayra.ch/tracker/list.php?prot[]=https&prot[]=http") .Result; if (Result.IsSuccessStatusCode) { TorrentHandler.PublicTrackers = new List <string>(Result .Content.ReadAsStringAsync().Result.Split('\n') .Select(m => m.Trim()) .Where(m => !string.IsNullOrEmpty(m)) .Distinct()); } else { throw new Exception("Unable to download Trackers"); } } catch { Console.WriteLine("Unable to download public tracker list"); Thread.Sleep(5000); } } Handler = new List <TorrentHandler>(); //Allows adding torrents via pipe Pipe.HashAdded += delegate(string Hash) { var Entry = GetHandler(Hash); if (Entry != null) { lock (Handler) { //only add if not existing already if (!Handler.Any(m => m != null && m.InfoHash == Entry.InfoHash)) { Handler.Add(Entry); } } #if !DEBUG Entry.Start(); #endif } }; #region Initial Download Creator if (args.Length > 0) { if (!args.Contains("/?")) { foreach (var arg in args) { var HandlerEntry = GetHandler(arg); if (HandlerEntry == null) { Console.Error.WriteLine("Invalid Argument. Only file names, magnet links and hashes are supported."); WaitForExit(); return(RET.ARGUMENT_ERROR); } else { Handler.Add(HandlerEntry); } } } else { Console.Error.WriteLine(@"QuickTorrent.exe [TorrentFile [...]] [MagnetLink [...]] [InfoHash [...]] TorrentFile - Torrent file to download MagnetLink - Magnet link to download that can be found in the DHT network InfoHash - SHA1 hash of a torrent that can be found in the DHT network Without arguments the application tries to interpret its file name as a hash."); WaitForExit(); return(RET.HELP); } } else { var Hash = Process.GetCurrentProcess().MainModule.FileName.Split(Path.DirectorySeparatorChar).Last().Split('.')[0]; if (ValidHex(Hash)) { Handler.Add(new TorrentHandler(ParseHash(Hash))); } else { Console.Error.WriteLine("No arguments given and file name not valid Infohash"); WaitForExit(); return(RET.NAME_ERROR); } } #endregion for (int i = 0; i < Handler.Count; i++) { if (Handler[i] == null) { Handler.RemoveAt(i--); } } //If we can't start a pipe it is likely that there is already an instance running if (!Pipe.StartPipe()) { //Try to transfer all downloads if (!Handler.Select(m => Pipe.SendViaPipe(m.InfoHash)).ToArray().Any(m => m)) { //We can neither start the pipe nor transfer requests to another client. Console.Error.WriteLine("Unable to start pipe or transfer downloads to other instance. Adding torrents at Runtime will be unavailable"); Thread.Sleep(5000); } else { //Requests transferred. Exit application return(RET.TRANSFER); } } Console.Clear(); #if !DEBUG foreach (var H in Handler) { H.Start(); } #endif //Thread will exit if set to false bool cont = true; //Exists the Wait loops and updates the screen once bool update = false; //Completely redraws the screen once bool redraw = false; //Selected Torrent int Selected = 0; Thread T = new Thread(delegate() { #region Torrent Loop const int NAMELENGTH = 30; while (cont && Handler.Count(m => m != null) > 0) { Console.Title = $"QuickTorrent: {Handler.Count(m => m != null)} transfers"; //Pause this thread while in the tracker selection while (CurrentMode == DisplayMode.Tracker) { Thread.Sleep(100); } int CurrentSelected = Selected; if (redraw) { Console.Clear(); redraw = false; } else { Console.SetCursorPosition(0, 0); } lock (Handler) { //var H = Handler[j]; switch (CurrentMode) { case DisplayMode.Overview: for (int j = 0; j < Handler.Count; j++) { if (Handler[j] != null) { string Name = Handler[j].TorrentName == null ? "" : Handler[j].TorrentName; //Subtraction is: name length, percentage and the 4 spaces var LineMap = new string(StretchMap(Handler[j].Map, Console.BufferWidth - NAMELENGTH - 8).Select(m => m ? '█' : '░').ToArray()); if (Name.Length > NAMELENGTH) { Name = Name.Substring(Name.Length - NAMELENGTH, NAMELENGTH); } Console.ForegroundColor = ConsoleColor.White; Console.Error.Write("{0} ", Selected == j ? '►' : ' '); Console.ForegroundColor = StateToColor(Handler[j].State); switch (Handler[j].State) { case TorrentState.Metadata: break; case TorrentState.Hashing: break; case TorrentState.Downloading: if (Handler[j].HasAllPieces && (int)Handler[j].Progress == 100) { Handler[j].Stop(); Handler[j].SaveRecovery(); Handler[j].Start(); } break; case TorrentState.Seeding: if (!Handler[j].HasAllPieces) { //Assume that this torrent is complete Handler[j].SetComplete(); } break; case TorrentState.Stopped: case TorrentState.Stopping: break; case TorrentState.Paused: break; default: break; } Console.Error.Write("{0,-" + NAMELENGTH + "} {1,3}% {2}", Name, (int)Handler[j].Progress, LineMap); } } Console.ResetColor(); Console.Error.WriteLine("[↑↓] Select | [SPACE] Start/Stop | [ENTER] Detail | [T] Tracker | [DEL] Delete | [ESC] Exit"); break; case DisplayMode.Details: Console.ForegroundColor = StateToColor(Handler[Selected].State); Console.Error.WriteLine("".PadRight(Console.BufferWidth * 7)); Console.SetCursorPosition(0, 0); Console.Error.WriteLine(@"Transfer Detail Name: {0} Hash: {1} Files: {2,-5} ({3}) State: {4,-20} Progress: {5:0.00}%", Handler[Selected].TorrentName.PadRight(Console.BufferWidth - 11), Handler[Selected].InfoHash, Handler[Selected].Files, NiceSize(Handler[Selected].TotalSize), Handler[Selected].State, Math.Round(Handler[Selected].Progress, 2)); var FullMap = new string(StretchMap(Handler[Selected].Map, Console.BufferWidth * (Console.WindowHeight - 8)).Select(m => m ? '█' : '░').ToArray()); Console.Error.Write("{0}[ESC] Back | [T] Tracker", FullMap); Console.ResetColor(); break; case DisplayMode.Tracker: //Don't do anything on tracker, this is done outside of the update loop break; default: throw new NotImplementedException($"Unimplemented Mode: {CurrentMode}"); } } int i = 0; //This makes the thread responsive to exit calls (cont=false) while (cont && i < UI_UPDATE_INTERVAL && CurrentSelected == Selected && !update) { i += 100; Thread.Sleep(100); } update = false; } #endregion }) { IsBackground = true, Name = "StatusLoop" }; T.Start(); #region Keyboard Handler while (cont && Handler.Count(m => m != null) > 0) { var H = Handler[Selected]; switch (Console.ReadKey(true).Key) { case ConsoleKey.T: var OldMode = CurrentMode; CurrentMode = DisplayMode.Tracker; ManageTracker(H); CurrentMode = OldMode; redraw = update = true; break; case ConsoleKey.Escape: if (CurrentMode == DisplayMode.Details || CurrentMode == DisplayMode.Tracker) { CurrentMode = DisplayMode.Overview; redraw = update = true; } else if (CurrentMode == DisplayMode.Overview) { cont = false; } break; case ConsoleKey.UpArrow: if (--Selected < 0) { Selected = 0; } update = true; break; case ConsoleKey.DownArrow: if (++Selected >= Handler.Count) { Selected = Handler.Count - 1; } update = true; break; case ConsoleKey.Spacebar: if (H.State == TorrentState.Stopped || H.State == TorrentState.Error) { H.Start(); update = true; } else if (H.State != TorrentState.Stopping && H.State != TorrentState.Error && H.State != TorrentState.Paused) { H.Stop(); update = true; } break; case ConsoleKey.Delete: lock (Handler) { H.Stop(); H.ClearRecovery(); H.Dispose(); Handler.Remove(H); } if (Handler.Count > 0) { if (Selected >= Handler.Count) { Selected = Handler.Count - 1; } } else { cont = false; } update = redraw = true; break; case ConsoleKey.Enter: CurrentMode = DisplayMode.Details; update = true; break; } } #endregion T.Join(); Console.Error.WriteLine("Exiting..."); TorrentHandler.StopAll(); foreach (var H in Handler.Where(m => m != null)) { Console.Error.Write('.'); H.SaveRecovery(); H.Dispose(); } TorrentHandler.SaveDhtNodes(); Console.Error.WriteLine("DONE. Cleaning up..."); return(RET.SUCCESS); }
public static int Main(string[] args) { LaunchedFromCmd = (ParentProcessUtilities.GetParentProcessName().ToLower() == Environment.ExpandEnvironmentVariables("%COMSPEC%").ToLower()); #if DEBUG args = new string[] { /* <-- Use two slashes to swap the hashes below * * //Debian DVD images * "40F90995A1C16A1BF454D09907F57700F3E8BD64", * "86F72E6B829C7E2D089B5CF0D1DED09122E65CA4", * "E355364EBEB5838B27705C68F85B20A950110B8F" * * /*/ //Debian CD images "A7055D06E5A8F7F816EC01AC7F7F5243D3CB008F", "63515D99E2A99E79526E35C9A39A1DFD843027A0", "297ACD1A5D6BA3E1AB881E27ACB73843A6E81430", "9E2C34C3FD30D25FED9A23D93FCC69AE546C1D10", "A887676B3E790DD28A92772B776199529F477762", "7980011D310EB8B6F8F5F593E82F83510901CE1C", "90C52B25375F12432D504BAD7F587E5543F22FDA", "1DADBB8451DDDF23ADB276D0C887EF60EC49989E" //*/ }; #endif Handler = new List <TorrentHandler>(); if (args.Length > 0) { if (!args.Contains("/?")) { foreach (var arg in args) { if (arg.ToLower().StartsWith("magnet:")) { Handler.Add(new TorrentHandler(ParseLink(arg))); } else if (File.Exists(arg)) { Handler.Add(new TorrentHandler(ParseTorrent(arg))); } else if (ValidHex(arg)) { Handler.Add(new TorrentHandler(ParseHash(arg))); } else { Console.Error.WriteLine("Invalid Argument. Only file names, magnet links and hashes are supported."); WaitForExit(); return(RET.ARGUMENT_ERROR); } } } else { Console.Error.WriteLine(@"QuickTorrent.exe [TorrentFile [...]] [MagnetLink [...]] [InfoHash [...]] TorrentFile - Torrent file to download MagnetLink - Magnet link to download that can be found in the DHT network InfoHash - SHA1 hash of a torrent that can be found in the DHT network Without arguments the application tries to interpret its file name as a hash."); WaitForExit(); return(RET.HELP); } } else { var Hash = Process.GetCurrentProcess().MainModule.FileName.Split(Path.DirectorySeparatorChar).Last().Split('.')[0]; if (ValidHex(Hash)) { Handler.Add(new TorrentHandler(ParseHash(Hash))); } else { Console.Error.WriteLine("No arguments given and file name not valid Infohash"); WaitForExit(); return(RET.NAME_ERROR); } } for (int i = 0; i < Handler.Count; i++) { if (Handler[i] == null) { Handler.RemoveAt(i--); } } TorrentHandler.StartAll(); Console.Title = $"QuickTorrent: {Handler.Count(m => m != null)} transfers"; bool cont = true; bool update = false; int Selected = 0; bool RenderDetail = false; Thread T = new Thread(delegate() { const int NAMELENGTH = 30; while (cont) { int CurrentSelected = Selected; Console.SetCursorPosition(0, 0); if (!RenderDetail) { for (int j = 0; j < Handler.Count; j++) { var H = Handler[j]; string Name = H.TorrentName == null ? "" : H.TorrentName; //Subtraction is: name length, percentage and the 4 spaces var Map = new string(StretchMap(H.Map, Console.BufferWidth - NAMELENGTH - 8).Select(m => m ? '█' : '░').ToArray()); if (Name.Length > NAMELENGTH) { Name = Name.Substring(Name.Length - NAMELENGTH, NAMELENGTH); } Console.ForegroundColor = ConsoleColor.White; Console.Error.Write("{0} ", Selected == j ? '►' : ' '); Console.ForegroundColor = StateToColor(H.State); switch (H.State) { case TorrentState.Metadata: break; case TorrentState.Hashing: break; case TorrentState.Downloading: if (H.HasAllPieces && (int)H.Progress == 100) { H.Stop(); H.SaveRecovery(); H.Start(); } break; case TorrentState.Seeding: if (!H.HasAllPieces) { //Assume that this torrent is complete H.SetComplete(); } break; case TorrentState.Stopped: case TorrentState.Stopping: break; case TorrentState.Paused: break; default: break; } Console.Error.Write("{0,-" + NAMELENGTH + "} {1,3}% {2}", Name, (int)H.Progress, Map); } Console.ResetColor(); Console.Error.WriteLine("[↑↓] Select | [SPACE] Start/Stop | [ENTER] Detail | [ESC] Exit"); } else { var H = Handler[Selected]; Console.ForegroundColor = StateToColor(H.State); Console.Error.WriteLine(@"Transfer Detail Name: {0} Hash: {1} Files: {2} ({3}) State: {4,-20} Progress: {5:0.00}%", H.TorrentName, H.InfoHash, H.Files, NiceSize(H.TotalSize), H.State, Math.Round(H.Progress, 2)); var Map = new string(StretchMap(H.Map, Console.BufferWidth * (Console.WindowHeight - 8)).Select(m => m ? '█' : '░').ToArray()); Console.Error.Write("{0}[ESC] Back", Map); Console.ResetColor(); } int i = 0; //This makes the thread responsive to exit calls (cont=false) while (cont && i < UI_UPDATE_INTERVAL && CurrentSelected == Selected && !update) { i += 100; Thread.Sleep(100); } update = false; } }) { IsBackground = true, Name = "Status" }; T.Start(); while (cont) { var H = Handler[Selected]; switch (Console.ReadKey(true).Key) { case ConsoleKey.Escape: if (RenderDetail) { RenderDetail = false; Console.Clear(); update = true; } else { cont = false; } break; case ConsoleKey.UpArrow: if (--Selected < 0) { Selected = 0; } else if (RenderDetail) { Console.Clear(); } break; case ConsoleKey.DownArrow: if (++Selected >= Handler.Count) { Selected = Handler.Count - 1; } else if (RenderDetail) { Console.Clear(); } break; case ConsoleKey.Spacebar: if (H.State == TorrentState.Stopped) { H.Start(); update = true; } else if (H.State != TorrentState.Stopping && H.State != TorrentState.Error && H.State != TorrentState.Paused) { H.Stop(); update = true; } break; case ConsoleKey.Enter: RenderDetail = true; Console.Clear(); update = true; break; } } T.Join(); Console.Error.WriteLine("Exiting..."); TorrentHandler.StopAll(); foreach (var H in Handler.Where(m => m != null)) { Console.Error.Write('.'); H.SaveRecovery(); H.Dispose(); } TorrentHandler.SaveDhtNodes(); Console.Error.WriteLine("DONE. Cleaning up..."); return(RET.SUCCESS); }