-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
67 changed files
with
353 additions
and
1,890 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
namespace HoradotTV.Console; | ||
|
||
internal static class Constants | ||
{ | ||
public static int QUERY_MIN_LENGTH = 2; | ||
|
||
public static string DEFAULT_DOWNLOAD_LOCATION = KnownFolders.Downloads.Path; | ||
} | ||
|
||
internal static class Menus | ||
{ | ||
public static string MODES_MENU = "-- Download Modes --\n" + | ||
"[0] Back to start\n" + | ||
"[1] Download Episode\n" + | ||
"[2] Download Episodes\n" + | ||
"[3] Download Season\n" + | ||
"[4] Download Series"; | ||
} | ||
|
||
internal enum Modes | ||
{ | ||
None, | ||
Episode, | ||
Episodes, | ||
Season, | ||
Series | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
global using OpenQA.Selenium; | ||
global using SdarotAPI; | ||
global using SdarotAPI.Models; | ||
global using Syroot.Windows.IO; | ||
global using System.Runtime.InteropServices; | ||
global using System.Text; |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Syroot.Windows.IO.KnownFolders" Version="1.3.0" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\SdarotAPI\SdarotAPI.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
namespace HoradotTV.Console; | ||
|
||
internal static class IOHelpers | ||
{ | ||
public static void Print(string s) => System.Console.WriteLine(s); | ||
public static string Input(string s = "") | ||
{ | ||
System.Console.Write(s); | ||
return System.Console.ReadLine()!; | ||
} | ||
public static int InputInt(string s = "") | ||
{ | ||
while (true) | ||
{ | ||
try | ||
{ | ||
return int.Parse(Input(s)); | ||
} | ||
catch (FormatException) | ||
{ | ||
Print("Please enter a number."); | ||
continue; | ||
} | ||
} | ||
} | ||
|
||
public static string ChooseOption(IEnumerable<string> options, string type = "option", string title = "Choose an option") | ||
{ | ||
string option; | ||
do | ||
{ | ||
Print($"\nAvailable {type}s: " + string.Join(", ", options)); | ||
option = Input($"{title}: "); | ||
if (options.Contains(option)) | ||
return option; | ||
Print($"Please enter an valid {type}."); | ||
} while (true); | ||
} | ||
public static int ChooseOptionRange(int max, string title = "Choose a number") => ChooseOptionRange(0, max, title); | ||
public static int ChooseOptionRange(int min, int max, string title = "Choose a number") | ||
{ | ||
int option; | ||
do | ||
{ | ||
option = InputInt($"{title} ({min}-{max}): "); | ||
|
||
if (option > max || option < min) | ||
{ | ||
Print("Please choose a number within the range."); | ||
} | ||
} while (option > max || option < min); | ||
|
||
return option; | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
using System.Linq; | ||
|
||
namespace HoradotTV.Console; | ||
|
||
internal class Program | ||
{ | ||
[DllImport("Kernel32")] | ||
private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add); | ||
|
||
private delegate bool EventHandler(CtrlType sig); | ||
static EventHandler? _handler; | ||
|
||
enum CtrlType | ||
{ | ||
CTRL_C_EVENT = 0, | ||
CTRL_BREAK_EVENT = 1, | ||
CTRL_CLOSE_EVENT = 2, | ||
CTRL_LOGOFF_EVENT = 5, | ||
CTRL_SHUTDOWN_EVENT = 6 | ||
} | ||
|
||
private static bool Handler(CtrlType sig) | ||
{ | ||
switch (sig) | ||
{ | ||
case CtrlType.CTRL_C_EVENT: | ||
case CtrlType.CTRL_LOGOFF_EVENT: | ||
case CtrlType.CTRL_SHUTDOWN_EVENT: | ||
case CtrlType.CTRL_CLOSE_EVENT: | ||
default: | ||
driver?.Shutdown(); | ||
break; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
static SdarotDriver? driver; | ||
|
||
static async Task Main() | ||
{ | ||
IOHelpers.Print("Welcome to HoradotTV!"); | ||
IOHelpers.Print("Initializing..."); | ||
|
||
_handler += new EventHandler(Handler); | ||
SetConsoleCtrlHandler(_handler, true); | ||
System.Console.InputEncoding = Encoding.Unicode; | ||
System.Console.OutputEncoding = Encoding.Unicode; | ||
driver = new SdarotDriver(); | ||
await driver.Initialize(); | ||
|
||
while (true) | ||
{ | ||
var series = await SearchSeries(driver); | ||
if (series == null) | ||
continue; | ||
|
||
var downloadLocation = GetDownloadLocation(); | ||
|
||
var mode = GetMode(); | ||
if (mode == Modes.None) | ||
continue; | ||
if (mode == Modes.Series) | ||
{ | ||
await DownloadSeries(driver, series, downloadLocation); | ||
continue; | ||
} | ||
|
||
var season = await GetSeason(driver, series); | ||
if (mode == Modes.Season) | ||
{ | ||
await DownloadSeason(driver, season, downloadLocation); | ||
continue; | ||
} | ||
|
||
var episode = await GetEpisode(driver, season); | ||
if (mode == Modes.Episode) | ||
{ | ||
await DownloadEpisode(driver, episode, downloadLocation); | ||
continue; | ||
} | ||
|
||
var episodesAmount = GetEpisodesAmount(); | ||
await DownloadEpisodes(driver, episode, episodesAmount, downloadLocation); | ||
} | ||
} | ||
|
||
static async Task<SeriesInformation?> SearchSeries(SdarotDriver driver) | ||
{ | ||
var searchResult = Array.Empty<SeriesInformation>(); | ||
do | ||
{ | ||
var query = IOHelpers.Input("\nEnter series name or part of it: "); | ||
if (query.Length < Constants.QUERY_MIN_LENGTH) | ||
{ | ||
IOHelpers.Print($"Please enter at least {Constants.QUERY_MIN_LENGTH} characters."); | ||
continue; | ||
} | ||
|
||
searchResult = await driver.SearchSeries(query); | ||
if (searchResult.Length == 0) | ||
IOHelpers.Print("Series not found."); | ||
} while (searchResult.Length == 0); | ||
|
||
IOHelpers.Print("\nResults:"); | ||
IOHelpers.Print("[0] Back to start"); | ||
for (var i = 0; i < searchResult.Length; i++) | ||
{ | ||
IOHelpers.Print($"[{i + 1}] {searchResult[i].SeriesNameEn} - {searchResult[i].SeriesNameHe}"); | ||
} | ||
|
||
var selection = IOHelpers.ChooseOptionRange(searchResult.Length, "Choose a series"); | ||
return selection == 0 ? null : searchResult[selection - 1]; | ||
} | ||
|
||
static string GetDownloadLocation() | ||
{ | ||
string path; | ||
do | ||
{ | ||
path = IOHelpers.Input($"\nEnter path for download (empty - {Constants.DEFAULT_DOWNLOAD_LOCATION}): ").Trim(); | ||
if (string.IsNullOrWhiteSpace(path)) | ||
path = Constants.DEFAULT_DOWNLOAD_LOCATION; | ||
|
||
try | ||
{ | ||
var fullPath = Path.GetFullPath(path); | ||
if (!File.GetAttributes(fullPath).HasFlag(FileAttributes.Directory)) | ||
{ | ||
IOHelpers.Print("Please enter a path to a directory."); | ||
path = ""; | ||
} | ||
} | ||
catch | ||
{ | ||
IOHelpers.Print("Please enter a valid path."); | ||
path = ""; | ||
} | ||
} while (string.IsNullOrWhiteSpace(path)); | ||
|
||
return path; | ||
} | ||
|
||
static Modes GetMode() | ||
{ | ||
IOHelpers.Print("\n" + Menus.MODES_MENU); | ||
return (Modes)IOHelpers.ChooseOptionRange(Enum.GetNames(typeof(Modes)).Length - 1, "Choose a mode"); | ||
} | ||
|
||
static async Task<SeasonInformation> GetSeason(SdarotDriver driver, SeriesInformation series) | ||
{ | ||
var seasons = await driver.GetSeasonsAsync(series); | ||
var seasonName = IOHelpers.ChooseOption(seasons.Select(s => s.SeasonName), "season", "Choose a season"); | ||
var season = seasons.Where(s => s.SeasonName == seasonName).First(); | ||
return season; | ||
} | ||
static async Task<EpisodeInformation> GetEpisode(SdarotDriver driver, SeasonInformation season) | ||
{ | ||
var episodes = await driver.GetEpisodesAsync(season); | ||
var episodeName = IOHelpers.ChooseOption(episodes.Select(e => e.EpisodeName), "episode", "Choose an episode"); | ||
var episode = episodes.Where(e => e.EpisodeName == episodeName).First(); | ||
return episode; | ||
} | ||
|
||
static int GetEpisodesAmount() | ||
{ | ||
var amount = IOHelpers.InputInt("\nEnter episodes amount: "); | ||
while (amount <= 0) | ||
{ | ||
IOHelpers.Print("Please enter a positive amount."); | ||
amount = IOHelpers.InputInt("Enter episodes amount: "); | ||
} | ||
|
||
return amount; | ||
} | ||
|
||
static async Task DownloadSeries(SdarotDriver driver, SeriesInformation series, string downloadLocation) => await DownloadEpisodes(driver, await driver.GetEpisodesAsync(series), downloadLocation); | ||
static async Task DownloadSeason(SdarotDriver driver, SeasonInformation season, string downloadLocation) => await DownloadEpisodes(driver, await driver.GetEpisodesAsync(season), downloadLocation); | ||
static async Task DownloadEpisode(SdarotDriver driver, EpisodeInformation episode, string downloadLocation) => await DownloadEpisodes(driver, new EpisodeInformation[] { episode }, downloadLocation); | ||
static async Task DownloadEpisodes(SdarotDriver driver, EpisodeInformation episode, int episodesAmount, string downloadLocation) => await DownloadEpisodes(driver, await driver.GetEpisodesAsync(episode, episodesAmount), downloadLocation); | ||
static async Task DownloadEpisodes(SdarotDriver driver, EpisodeInformation[] episodes, string downloadLocation) | ||
{ | ||
for (var i = 0; i < episodes.Length; i++) | ||
{ | ||
var episode = episodes[i]; | ||
IOHelpers.Print($"\n({i + 1}/{episodes.Length})"); | ||
IOHelpers.Print($"Loading {episode.Season.SeasonString} {episode.EpisodeString}..."); | ||
var episodeMedia = await GetEpisodeMediaDetails(driver, episode); | ||
if (episodeMedia == null) | ||
{ | ||
IOHelpers.Print("Failed. Proceeding to next episode."); | ||
continue; | ||
} | ||
|
||
IOHelpers.Print($"Downloading {episode.Season.SeasonString} {episode.EpisodeString}..."); | ||
var cleanSeriesName = string.Concat(episode.Series.SeriesNameEn.Where(c => !Path.GetInvalidFileNameChars().Contains(c))); | ||
var finalLocation = Path.Combine(downloadLocation, cleanSeriesName, episode.Season.SeasonString, episode.EpisodeString + ".mp4"); | ||
Directory.CreateDirectory(Path.GetDirectoryName(finalLocation)!); | ||
await SdarotHelper.DownloadEpisode(episodeMedia, finalLocation); | ||
} | ||
|
||
IOHelpers.Print("\nDone. Returning to start."); | ||
} | ||
|
||
static async Task<EpisodeMediaDetails?> GetEpisodeMediaDetails(SdarotDriver driver, EpisodeInformation episode, int retries = 2) | ||
{ | ||
do | ||
{ | ||
try | ||
{ | ||
return await driver.GetEpisodeMediaDetailsAsync(episode); | ||
} | ||
catch (WebDriverTimeoutException) | ||
{ | ||
if (retries > 0) | ||
IOHelpers.Print($"Failed. Trying again... ({retries} tries left)"); | ||
retries--; | ||
} | ||
} while (retries > -1); | ||
|
||
return null; | ||
} | ||
} |
This file contains 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
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.