diff --git a/CCTV/CameraSearcher.cs b/CCTV/CameraSearcher.cs new file mode 100644 index 0000000..decb73f --- /dev/null +++ b/CCTV/CameraSearcher.cs @@ -0,0 +1,106 @@ +using Leaf.xNet; +using System.Drawing; +using System.Security.Cryptography.X509Certificates; +using System.Text.RegularExpressions; + +namespace OpenCCTV.CCTV +{ + internal class CameraSearcher + { + /// + /// Finds Cameras with ease. + /// + /// The country code + public static void FindCameras(string countryCode) + { + Console.Clear(); + Console.Title = $"CCTV Browser by Serialized - Searching Cameras in {countryCode}"; + Interface.InterfaceMaker.PrintLogo(); + + List camerasList = new List(); + + HttpRequest hr = new HttpRequest() + { + IgnoreProtocolErrors = true, + }; + + hr.SslCertificateValidatorCallback += (obj, cert, ssl, error) => (cert as X509Certificate2).Verify(); + + try + { + int lastPage = GetLastPage(hr, countryCode); + + for (int i = 0; i < lastPage; i++) + { + string[] cameras = GetCameras(hr, countryCode, i); + + for (int j = 0; j < cameras.Length; j++) + { + camerasList.Add(cameras[j]); + Interface.InterfaceMaker.WriteLine(Color.White, $"{countryCode}-{camerasList.Count}", $"{cameras[j]}\n"); + } + } + + Console.WriteLine(); + Interface.InterfaceMaker.WriteLine(Color.White, "A", $"Export All Cameras\n"); + Interface.InterfaceMaker.WriteLine(Color.White, "B", $"Open All Cameras\n"); + Interface.InterfaceMaker.WriteLine(Color.White, "C", $"Back\n"); + Interface.InterfaceMaker.WriteLine(Color.White, ">", $""); + + string userInput = Console.ReadLine(); + + switch (userInput) + { + case "A": + File.WriteAllLines($"{countryCode}.txt", camerasList); + Interface.InterfaceMaker.WriteLine(Color.White, "~", "Exported All Cameras!\n"); + Utils.CountryMenu(); + break; + + case "B": + foreach (string cameras in camerasList) + Program.OpenWebpage(cameras); + + break; + + case "C": + Utils.CountryMenu(); + break; + } + } + catch (Exception ex) + { + Interface.InterfaceMaker.WriteLine(Color.White, "Error", "Couldn't Obtain Cameras.\n"); + } + } + + /// + /// Checks how many pages there are to check + /// + /// The HttpRequest + /// The country + /// The number of pages + private static int GetLastPage(HttpRequest hr, string country) + { + var camResp = hr.Get($"http://www.insecam.org/en/bycountry/{country}"); + var lastPageMatch = Regex.Match(camResp.ToString(), @"pagenavigator\(""\?page="", (\d+)"); + return lastPageMatch.Success ? int.Parse(lastPageMatch.Groups[1].Value) : 0; + } + + /// + /// Gets the list of open cameras. + /// + /// + /// + /// + /// A list of cameras + private static string[] GetCameras(HttpRequest hr, string country, int page) + { + var camResp = hr.Get($"http://www.insecam.org/en/bycountry/{country}/?page={page}"); + return Regex.Matches(camResp.ToString(), @"http://\d+.\d+.\d+.\d+:\d+") + .Cast() + .Select(match => match.Value) + .ToArray(); + } + } +} \ No newline at end of file diff --git a/CCTV/CountrySearcher.cs b/CCTV/CountrySearcher.cs new file mode 100644 index 0000000..0a0e7dc --- /dev/null +++ b/CCTV/CountrySearcher.cs @@ -0,0 +1,49 @@ +using Leaf.xNet; +using Newtonsoft.Json; +using System.Drawing; +using System.Security.Cryptography.X509Certificates; + +namespace OpenCCTV.CCTV +{ + internal class CountrySearcher + { + /// + /// Displays the amount of cameras per country. + /// + public static void DisplayCountryInfo() + { + HttpRequest hr = new HttpRequest() + { + IgnoreProtocolErrors = true, + }; + + hr.SslCertificateValidatorCallback += (obj, cert, ssl, error) => (cert as X509Certificate2).Verify(); + + try + { + HttpResponse countryResp = hr.Get("http://www.insecam.org/en/jsoncountries/"); + string countryJson = countryResp.ToString(); + var countryData = JsonConvert.DeserializeObject(countryJson); + + if (countryData != null && countryData.Countries != null) + { + foreach (var country in countryData.Countries) + { + if (country.Value.Country == "-") continue; + + Interface.InterfaceMaker.PrintCountryInfo( + country.Key.PadRight(2), + country.Value.Country.PadRight(25), + country.Value.Count); + } + } + + Console.WriteLine(); + } + catch (Exception ex) + { + Interface.InterfaceMaker.WriteLine(Color.White, "Error", "Couldn't obtain Country Statistics.\n"); + } + } + } +} \ No newline at end of file diff --git a/CCTV/Utils.cs b/CCTV/Utils.cs new file mode 100644 index 0000000..f581c6b --- /dev/null +++ b/CCTV/Utils.cs @@ -0,0 +1,55 @@ +using System.Drawing; + +namespace OpenCCTV.CCTV +{ + /// + /// Utility class containing various visible output methods. + /// + internal class Utils + { + /// + /// Menu that helps displaying country statistics and user input. + /// + public static void CountryMenu() + { + SetConsoleTitle("CCTV Browser by Serialized - Displaying Information By Country"); + Interface.InterfaceMaker.WriteLine(Color.White, "~", "Retrieving Country Statistics\n\n"); + + /* Display the fetched information. */ + CountrySearcher.DisplayCountryInfo(); + + Interface.InterfaceMaker.WriteLine(Color.White, "~", "Enter Your Wanted Country\n"); + Interface.InterfaceMaker.WriteLine(Color.White, ">", ""); + + Console.ForegroundColor = ConsoleColor.White; + string countryInput = Console.ReadLine(); + CameraSearcher.FindCameras(countryInput); + } + + /// + /// Set Console.Title and Flush Console. + /// + /// The consoles title. + private static void SetConsoleTitle(string title) + { + Console.Clear(); + Console.Title = title; + Interface.InterfaceMaker.PrintLogo(); + } + + /// + /// Deserializable class holding Country Information + /// + public class Country + { + public string? Status { get; set; } + public Dictionary? Countries { get; set; } + } + + public class CountryInfo + { + public string? Country { get; set; } + public int Count { get; set; } + } + } +} \ No newline at end of file diff --git a/Interface/InterfaceMaker.cs b/Interface/InterfaceMaker.cs new file mode 100644 index 0000000..e5b9fc0 --- /dev/null +++ b/Interface/InterfaceMaker.cs @@ -0,0 +1,119 @@ +using System.Drawing; +using Console = Colorful.Console; + +namespace OpenCCTV.Interface +{ + /// + /// Provides methods for the interface. + /// + public class InterfaceMaker + { + /// + /// Represents the ASCII 'header' for the application. + /// + private static readonly string[] _asciiLogo = + { + " ", + " ██████╗ ██████╗ ███████╗███╗ ██╗ ██████╗ ██████╗████████╗██╗ ██╗", + "██╔═══██╗██╔══██╗██╔════╝████╗ ██║██╔════╝██╔════╝╚══██╔══╝██║ ██║", + "██║ ██║██████╔╝█████╗ ██╔██╗ ██║██║ ██║ ██║ ██║ ██║", + "██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║██║ ██║ ██║ ╚██╗ ██╔╝", + "╚██████╔╝██║ ███████╗██║ ╚████║╚██████╗╚██████╗ ██║ ╚████╔╝ ", + " ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝ ", + " ", + }; + + /// + /// Represents the colors for the ASCII logo. + /// + private static readonly List _colorList = new List() + { + Color.White, + ColorTranslator.FromHtml("#FCEE2B"), + ColorTranslator.FromHtml("#E2D626"), + ColorTranslator.FromHtml("#C8BD21"), + ColorTranslator.FromHtml("#AFA51D"), + ColorTranslator.FromHtml("#958C18"), + ColorTranslator.FromHtml("#7B7413"), + }; + + /// + /// Prints the ASCII logo to the console with customizable colors (scheme set to yellow -> black). + /// + public static void PrintLogo() + { + for (int i = 0; i < _asciiLogo.Length - 1; i++) + { + Console.SetCursorPosition(Console.WindowWidth / 2 - _asciiLogo[0].Length / 2, i); + Console.WriteLine(_asciiLogo[i], _colorList[i]); + + switch (i) + { + case 1: + Colors.Primary = _colorList[i]; + break; + + case 2: + Colors.Secondary = _colorList[i]; + break; + } + } + + // Bottom padding. + Console.WriteLine(); + Console.WriteLine(); + Console.WriteLine(); + } + + /// + /// Override method to display formatted and colorized WriteLine. + /// + /// The color of the message. + /// The prefix of the message. + /// The message to be displayed. + public static void WriteLine(Color color, string prefix, string msg) + { + Console.Write(" [", Colors.Secondary); + Console.Write(prefix, Color.White); + Console.Write("]", Colors.Secondary); + Console.Write(" → ", Color.White); + Console.Write(msg, color); + } + + /// + /// Method to print country information formatted and colorized. + /// + /// The two characters to identify a country. + /// The name of the country. + /// The count of cameras. + public static void PrintCountryInfo(string countryCode, string countryName, int count) + { + Console.Write(" [", Colors.Secondary); + Console.Write($"{countryCode}", Color.White); + Console.Write("]", Colors.Secondary); + Console.Write(" → ", Color.White); + Console.Write($"{countryName}", Colors.Primary); + Console.Write(" → ", Color.White); + + switch (count == 1) + { + case true: + Console.WriteLine(count + " Camera", Color.White); + break; + + case false: + Console.WriteLine(count + " Cameras", Color.White); + break; + } + } + } + + /// + /// Provides Color variables for the interface. + /// + public class Colors + { + public static Color Primary { get; set; } + public static Color Secondary { get; set; } + } +} \ No newline at end of file diff --git a/OpenCCTV.csproj b/OpenCCTV.csproj new file mode 100644 index 0000000..83acc52 --- /dev/null +++ b/OpenCCTV.csproj @@ -0,0 +1,16 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + + diff --git a/OpenCCTV.sln b/OpenCCTV.sln new file mode 100644 index 0000000..0a7d2b5 --- /dev/null +++ b/OpenCCTV.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34202.233 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenCCTV", "OpenCCTV.csproj", "{6E7AA6DD-E5E8-42C4-ADE8-A25C39A36C18}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6E7AA6DD-E5E8-42C4-ADE8-A25C39A36C18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6E7AA6DD-E5E8-42C4-ADE8-A25C39A36C18}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E7AA6DD-E5E8-42C4-ADE8-A25C39A36C18}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6E7AA6DD-E5E8-42C4-ADE8-A25C39A36C18}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5A07CEC4-DDD4-4118-AD8F-66F2A66E457C} + EndGlobalSection +EndGlobal diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..1436a75 --- /dev/null +++ b/Program.cs @@ -0,0 +1,77 @@ +using System.Diagnostics; +using System.Drawing; + +namespace OpenCCTV +{ + internal class Program + { + private static void Main(string[] args) + { + Console.Title = "CCTV Browser by Serialized"; + Console.CursorVisible = false; + + OpenMenu(); + Console.ReadKey(); /* Prevent from closing */ + } + + /// + /// Displays the main menu for OpenCCTV. + /// + private static void OpenMenu() + { + Console.Clear(); + Interface.InterfaceMaker.PrintLogo(); + Interface.InterfaceMaker.WriteLine(Color.White, "~", "Welcome to OpenCCTV - Select An Option\n\n"); + Interface.InterfaceMaker.WriteLine(Color.White, "1", "View Countries\n"); + Interface.InterfaceMaker.WriteLine(Color.White, "2", "Goto Repository\n"); + + ConsoleKey cki = getCki; + + switch (cki) + { + case ConsoleKey.NumPad1: + case ConsoleKey.D1: + CCTV.Utils.CountryMenu(); + break; + + case ConsoleKey.NumPad2: + case ConsoleKey.D2: + OpenWebpage("https://github.com/nak0823/OpenCCTV"); // Star the repo ;) + OpenMenu(); + break; + + default: + OpenMenu(); + break; + } + } + + /// + /// Opens a webpage using cmd. + /// + /// + public static void OpenWebpage(string url) + { + try + { + Process.Start(new ProcessStartInfo + { + FileName = "cmd", + Arguments = $"/c start {url}", + UseShellExecute = false, + RedirectStandardOutput = true, + CreateNoWindow = true + }); + } + catch (Exception ex) + { + Console.WriteLine($"Error: {ex.Message}"); + } + } + + /// + /// Returns the users selected ConsoleKey. + /// + private static ConsoleKey getCki => Console.ReadKey(true).Key; + } +} \ No newline at end of file