// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { var proxies = new List <string>(); Configuration.GetSection("TPBProxies").Bind(proxies); var sofakingConfiguration = new SoFakingConfiguration(); Configuration.GetSection("Sofaking").Bind(sofakingConfiguration); if (proxies.Count == 0) { throw new Exception("TPB Proxies configuration missing"); } TPBProxies.SetProxies(proxies.ToArray()); var transmissionConfiguration = new TransmissionConfiguration(); Configuration.GetSection("Transmission").Bind(transmissionConfiguration); services.AddHttpClient(); services.AddControllers().AddJsonOptions(opt => { opt.JsonSerializerOptions.PropertyNameCaseInsensitive = true; opt.JsonSerializerOptions.PropertyNamingPolicy = null; }); services.AddSingleton(transmissionConfiguration); services.AddSingleton <TransmissionHttpClientFactory>(); services.AddSingleton(new SoFakingContextFactory()); services.AddSingleton <MovieService>(); services.AddSingleton <TPBParserService>(); services.AddSingleton <ITorrentClientService, TransmissionService>(); services.AddSingleton <IVerifiedMovieSearchService, ImdbService>(); }
public FFMPEGEncoderService(ILogger <FFMPEGEncoderService> logger, EncoderConfiguration configuration, SoFakingConfiguration sofakingConfiguration) { _logger = logger; _configuration = configuration; _sofakingConfiguration = sofakingConfiguration; SetCancellationToken(); }
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseSystemd() // Linux service lifetime management .ConfigureAppConfiguration((context, builder) => { builder .SetBasePath(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)) // Required for Linux service .AddJsonFile("appsettings.json") .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json") .AddJsonFile("dbsettings.json") .AddJsonFile($"dbsettings.{context.HostingEnvironment.EnvironmentName}.json") .AddEnvironmentVariables() .Build(); }) .ConfigureServices((hostContext, services) => { var proxies = new List <string>(); hostContext.Configuration.GetSection("TPBProxies").Bind(proxies); if (proxies.Count == 0) { throw new Exception("TPB Proxies configuration missing"); } TPBProxies.SetProxies(proxies.ToArray()); var downloadFinishedWorkerConfiguration = new DownloadFinishedWorkerConfiguration(); hostContext.Configuration.GetSection("DownloadFinishedWorker").Bind(downloadFinishedWorkerConfiguration); var transmissionConfiguration = new TransmissionConfiguration(); hostContext.Configuration.GetSection("Transmission").Bind(transmissionConfiguration); var encoderConfiguration = new EncoderConfiguration(); hostContext.Configuration.GetSection("Encoder").Bind(encoderConfiguration); var sofakingConfiguration = new SoFakingConfiguration(); hostContext.Configuration.GetSection("Sofaking").Bind(sofakingConfiguration); services .AddHttpClient() .AddSingleton(transmissionConfiguration) .AddSingleton(sofakingConfiguration) .AddSingleton <TransmissionHttpClientFactory>() .AddSingleton(downloadFinishedWorkerConfiguration) .AddSingleton(encoderConfiguration) .AddSingleton(new SoFakingContextFactory()) .AddSingleton <MovieService>() .AddSingleton <TPBParserService>() .AddSingleton <ITorrentClientService, TransmissionService>() .AddSingleton <IVerifiedMovieSearchService, ImdbService>() //.AddSingleton<IEncoderService, FFMPEGEncoderService>() .AddHostedService <SoFakingWorker>(); });
public SoFakingWorker(ILogger <SoFakingWorker> logger, ILogger <FFMPEGEncoderService> loggerEnc, IHttpClientFactory clientFactory, DownloadFinishedWorkerConfiguration configuration, MovieService movieService, ITorrentClientService torrentClient, /* IEncoderService encoderService,*/ SoFakingConfiguration sofakingConfiguration, EncoderConfiguration encoderConfiguration) { if (clientFactory == null) { throw new ArgumentNullException(nameof(clientFactory)); } if (movieService == null) { throw new ArgumentNullException(nameof(movieService)); } if (configuration == null) { throw new ArgumentNullException(nameof(configuration)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } if (loggerEnc == null) { throw new ArgumentNullException(nameof(loggerEnc)); } if (torrentClient == null) { throw new ArgumentNullException(nameof(torrentClient)); } if (encoderConfiguration == null) { throw new ArgumentNullException(nameof(encoderConfiguration)); } if (sofakingConfiguration == null) { throw new ArgumentNullException(nameof(sofakingConfiguration)); } _sofakingConfiguration = sofakingConfiguration; _clientFactory = clientFactory; _movieService = movieService; _configuration = configuration; _logger = logger; _loggerEnc = loggerEnc; _torrentClient = torrentClient; _encoderConfiguration = encoderConfiguration; }
//private static ServiceProvider _serviceProvider; static async Task Main(string[] args) { IVerifiedMovie selectedMovie = null; var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true) // TODO: Change the Production with Enviroment .AddEnvironmentVariables(); IConfigurationRoot configuration = builder.Build(); var proxies = new List <string>(); configuration.GetSection("TPBProxies").Bind(proxies); var sofakingConfiguration = new SoFakingConfiguration(); configuration.GetSection("Sofaking").Bind(sofakingConfiguration); if (proxies.Count == 0) { throw new Exception("TPB Proxies configuration missing"); } TPBProxies.SetProxies(proxies.ToArray()); var transmissionConfiguration = new TransmissionConfiguration(); configuration.GetSection("Transmission").Bind(transmissionConfiguration); //TransmissionHttpClient.Configure(transmissionConfiguration) var builder1 = new HostBuilder() .ConfigureServices((hostContext, services) => { services .AddHttpClient() .AddSingleton(transmissionConfiguration) .AddSingleton <TransmissionHttpClientFactory>() .AddSingleton(new SoFakingContextFactory()) .AddSingleton <MovieService>() .AddSingleton <TPBParserService>() .AddSingleton <ITorrentClientService, TransmissionService>() .AddSingleton <IVerifiedMovieSearchService, ImdbService>(); }).UseConsoleLifetime(); var host = builder1.Build(); using (var serviceScope = host.Services.CreateScope()) { var serviceProvider = serviceScope.ServiceProvider; while (true) { var movieService = serviceProvider.GetService <MovieService>(); Console.Clear(); Console.ResetColor(); Console.WriteLine("Enter a movie name in English to look for: (CTRL+C to quit)"); var query = Console.ReadLine(); var verifiedMovieSearch = serviceProvider.GetService <IVerifiedMovieSearchService>(); var verifiedMovies = await verifiedMovieSearch.Search(query); var movieJobs = await movieService.GetMoviesAsync(); if (verifiedMovies == null || verifiedMovies.Count == 0) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("No results found."); Console.ResetColor(); Console.ReadKey(); continue; } if (verifiedMovies.Count == 1) { selectedMovie = verifiedMovies.ElementAt(0); } if (verifiedMovies.Count > 1) { Console.WriteLine("There are several matches:"); for (var i = 0; i < verifiedMovies.Count(); i++) { var vm = verifiedMovies.ElementAt(i); var status = string.Empty; var movieJob = movieJobs.Where(x => x.ImdbId == vm.Id).FirstOrDefault(); if (movieJob != null) { status = SetMovieConsoleStatus(movieJob); } Console.WriteLine($"[{(i+1)}]\t{vm.Score}/10\t{vm.ScoreMetacritic} Metacritic\t{status}\t{vm.Title.Utf8ToAscii()} ({vm.ReleaseYear})"); if (movieJob != null) { Console.ResetColor(); } } Console.WriteLine($"[1-{verifiedMovies.Count()}] Search torrents for download, Any key = New search, CTRL+C = quit"); if (int.TryParse(Console.ReadLine(), out int selectedMovieIndex)) { selectedMovie = verifiedMovies.ElementAt(selectedMovieIndex - 1); } else { continue; } } var torrentSearchService = serviceProvider.GetService <TPBParserService>(); var torrentQuery = $"{selectedMovie.Title} {selectedMovie.ReleaseYear}"; // TODO: get rid of this goto defaulttorrentsearch; #region Confirm torrent search query confirmtorrentsearchquery: { Console.Clear(); Console.WriteLine($"Torrent search query: {torrentQuery}. Overwrite? (empty to keep)"); var tqInput = Console.ReadLine(); if (tqInput.Trim() != string.Empty) { torrentQuery = tqInput; } Console.Clear(); } #endregion defaulttorrentsearch: { // Just skip ahead... } var foundTorrents = await torrentSearchService.Search(torrentQuery); if (foundTorrents.Count == 0) { Console.Write("\r\n"); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("No torrents found."); Console.ResetColor(); Console.WriteLine("\n"); Console.WriteLine($"Add to watchlist? [w], Cancel [n], CTRL+C = quit)"); if (Console.ReadLine() == "w") { try { var m = MergeMovie(selectedMovie); m.TorrentName = query; m.Status = MovieStatusEnum.WatchingFor; await movieService.AddMovie(m); continue; } catch (Exception ex) { Console.WriteLine($"Could not add Movie to the watchlist: {ex.Message}"); Console.ReadKey(); } } } Console.Clear(); for (var i = 0; i < foundTorrents.Count(); i++) { var t = foundTorrents.ElementAt(i); var status = string.Empty; var movieJob = movieJobs.Where(x => x.TorrentName == t.Name).FirstOrDefault(); if (movieJob != null) { status = SetMovieConsoleStatus(movieJob); } Console.WriteLine($"[{(i+1)}]\t{t.Seeders}\t{t.SizeGb}Gb\t{status}\t{t.Name}"); if (movieJob != null) { Console.ResetColor(); } } Console.WriteLine("\n"); var bestTorrent = TorrentRating.GetBestTorrent(foundTorrents, sofakingConfiguration.AudioLanguages); if (bestTorrent == null) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"None of the torrents above passed the limits (Seeders > 0, Size Gb > {TorrentScoring.FileSizeGbMin} < {TorrentScoring.FileSizeGbMax}), or contained a banned word: {string.Join(", ", TorrentScoring.Tags.Where(x => x.Value == TorrentScoring.BannedTag).Select(x => x.Key))}"); Console.ResetColor(); Console.WriteLine("\n"); Console.WriteLine($"Cancel? [n], Watchlist [w], [1-{(foundTorrents.Count() + 1)}] for manual selection, [c] for changing the search query, CTRL+C = quit)"); } else { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine($"Best torrent: ({bestTorrent.Seeders} Seeders), {bestTorrent.SizeGb}Gb {bestTorrent.Name}"); Console.ResetColor(); Console.WriteLine("\n"); Console.WriteLine($"Download the best torrent? [y/n], [1-{(foundTorrents.Count() + 1)}] for manual selection, [c] for changing the search query, CTRL+C = quit)"); } var selectedTorrent = bestTorrent; string input = Console.ReadLine(); if (input == "n") { continue; } if (input == "c") { goto confirmtorrentsearchquery; } if (input == "w") { try { var m = MergeMovie(selectedMovie); m.TorrentName = query; m.Status = MovieStatusEnum.WatchingFor; await movieService.AddMovie(m); Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Movie added to Watchlist."); Console.ResetColor(); Thread.Sleep(3 * 1000); continue; } catch (Exception ex) { Console.WriteLine($"Could not add Movie to the watchlist: {ex.Message}"); Console.ReadKey(); } continue; } if (int.TryParse(input, out int selectedTorrentIndex)) { selectedTorrent = foundTorrents.ElementAt(selectedTorrentIndex - 1); } if (selectedTorrent == null) { Console.WriteLine($"Selected torrent was null."); Console.ReadKey(); continue; } var transmission = serviceProvider.GetService <ITorrentClientService>(); try { var result = await transmission.AddTorrentAsync(selectedTorrent.MagnetLink); if (result == null) { throw new Exception("Could not add torrent to the torrent client."); } try { var m = MergeMovie(selectedMovie, selectedTorrent, result); if (!await movieService.AddMovie(m)) { throw new Exception("db failed"); } Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Movie added for download."); Console.ResetColor(); Thread.Sleep(3 * 1000); continue; } catch (Exception ex) { Console.WriteLine($"Could not add Movie to the catalog: {ex.Message}"); Console.ReadKey(); } } catch (Exception ex) { Console.WriteLine($"Could not add torrent to download: {ex.Message}"); Console.ReadKey(); } } } //_serviceProvider = new ServiceCollection() // .AddLogging() // .AddHttpClient() // .AddSingleton(transmissionConfiguration) // .AddSingleton(new SoFakingContextFactory()) // .AddSingleton<MovieService>() // .AddSingleton<TPBParserService>() // .AddSingleton<ITorrentClientService, TransmissionService>() // .AddSingleton<IVerifiedMovieSearchService, ImdbService>() // .BuildServiceProvider(); }
private static VideoCompatibilityFlags HasAcceptableVideo(DConf dc, SoFakingConfiguration sc, IMediaInfo mediaInfo) { if (dc.AcceptedVideoCodecs == null) { throw new ArgumentNullException(nameof(dc.AcceptedVideoCodecs)); } if (mediaInfo == null) { throw new ArgumentNullException(nameof(mediaInfo)); } if (mediaInfo.VideoCodec == null) { throw new ArgumentNullException(nameof(mediaInfo.VideoCodec)); } if (mediaInfo.FileInfo == null) { throw new ArgumentNullException(nameof(mediaInfo.FileInfo)); } if (mediaInfo.HorizontalVideoResolution == -1) { throw new ArgumentException($"Horizontal resolution invalid: {nameof(mediaInfo.HorizontalVideoResolution)}"); } var acceptableCodec = false; foreach (var vc in dc.AcceptedVideoCodecs) { if (mediaInfo.VideoCodec.IndexOf(vc) >= 0) { acceptableCodec = true; break; } } var acceptableResolution = mediaInfo.HorizontalVideoResolution <= sc.MaxHorizontalVideoResolution; var acceptableSize = mediaInfo.FileInfo.Length > (sc.MaxSizeGb * 1024 * 1024); // TODO: Fixing getting the video bitrate right would speed up the program significantly. // Unfortunately, FFMPEG can't return bitrate of only the video stream. So we will ONLY stream copy if video and all the audio streams combined have a lower bitrate than level 4.2 h264 video bitrate compatible with PS4 (6,25Mbit/s) var acceptableBitrate = (mediaInfo.AVBitrateKbs == null || mediaInfo.AVBitrateKbs <= TargetVideoBitrateKbs); var flags = VideoCompatibilityFlags.Compatible; if (!acceptableCodec) { flags |= VideoCompatibilityFlags.IncompatibleCodec; } if (!acceptableResolution) { flags |= VideoCompatibilityFlags.IncompatibleResolution; } if (!acceptableSize) { flags |= VideoCompatibilityFlags.IncompatibleSize; } if (!acceptableBitrate) { flags |= VideoCompatibilityFlags.IncompatibleBitrate; } return(flags); }
static async Task Main(string[] args) { if (args.Length == 0) { throw new Exception("No file to convert provided"); } Console.WriteLine("Encoding: " + args[0]); var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true) // TODO: Change the Production with Enviroment .AddEnvironmentVariables(); IConfigurationRoot configuration = builder.Build(); var encoderConfiguration = new EncoderConfiguration(); configuration.GetSection("Encoder").Bind(encoderConfiguration); var sofakingConfiguration = new SoFakingConfiguration(); configuration.GetSection("Sofaking").Bind(sofakingConfiguration); var dc = new DConf(); configuration.GetSection("DownloadFinishedWorker").Bind(dc); var builder1 = new HostBuilder() .ConfigureServices((hostContext, services) => { services .AddSingleton(sofakingConfiguration) .AddSingleton(encoderConfiguration) .AddSingleton(new SoFakingContextFactory()); }).UseConsoleLifetime(); var host = builder1.Build(); using (var serviceScope = host.Services.CreateScope()) { var done = false; var serviceProvider = serviceScope.ServiceProvider; var logger = new NullLogger <FFMPEGEncoderService>(); var flags = EncodingTargetFlags.None; var videoFile = args[0]; IMediaInfo mediaInfo = null; var useCuda = true; Console.Clear(); Console.WriteLine($"Is this a film (vs. cartoon)? [y/n]"); if (Console.ReadKey().KeyChar == 'n') { flags |= EncodingTargetFlags.VideoIsAnimation; } var encoderTranscodingInstance = new FFMPEGEncoderService(logger, encoderConfiguration, sofakingConfiguration); try { mediaInfo = await encoderTranscodingInstance.GetMediaInfo(videoFile); } catch (Exception ex) { Console.Clear(); Console.WriteLine("Can not read media info: " + ex.Message); Console.ReadKey(); return; } // Find subtitles var filenameBase = Path.GetFileNameWithoutExtension(videoFile); var path = Path.GetDirectoryName(videoFile); var subtitles = new Dictionary <string, string>(); foreach (var sl in sofakingConfiguration.SubtitleLanguages) { var subPath = Path.Combine(path, filenameBase + $".{sl}.srt"); if (File.Exists(subPath)) { subtitles.Add(sl, subPath); } } if (subtitles.Count > 0) { Console.Clear(); Console.WriteLine($"Found {subtitles.Count} subtitles to embed."); Thread.Sleep(3 * 1000); flags |= EncodingTargetFlags.ExternalSubtitles; } try { var videoAudit = HasAcceptableVideo(dc, sofakingConfiguration, mediaInfo); if (videoAudit != VideoCompatibilityFlags.Compatible) { Console.Clear(); Console.WriteLine("Video details:"); if (videoAudit.HasFlag(VideoCompatibilityFlags.IncompatibleCodec)) { Console.ForegroundColor = ConsoleColor.Red; } Console.WriteLine($" Codec: {mediaInfo.VideoCodec}, (Accepted: {string.Join(", ", dc.AcceptedVideoCodecs)})"); Console.ResetColor(); if (videoAudit.HasFlag(VideoCompatibilityFlags.IncompatibleResolution)) { Console.ForegroundColor = ConsoleColor.Red; } Console.WriteLine($" H. Resolution: {mediaInfo.HorizontalVideoResolution}px, (Max: {sofakingConfiguration.MaxHorizontalVideoResolution}px)"); Console.ResetColor(); if (videoAudit.HasFlag(VideoCompatibilityFlags.IncompatibleBitrate)) { Console.ForegroundColor = ConsoleColor.Red; } Console.WriteLine($" Avg bitrate: {(mediaInfo.AVBitrateKbs.HasValue ? ByteSize.FromKiloBytes(mediaInfo.AVBitrateKbs.Value).ToString() : "?")}/s, (Max: {ByteSize.FromKiloBytes(TargetVideoBitrateKbs)}/s"); Console.ResetColor(); if (videoAudit.HasFlag(VideoCompatibilityFlags.IncompatibleSize)) { Console.ForegroundColor = ConsoleColor.Red; } Console.WriteLine($" Size: {ByteSize.FromBytes(mediaInfo.FileInfo.Length)} (Max: {ByteSize.FromGigaBytes(sofakingConfiguration.MaxSizeGb)})"); Console.ResetColor(); Console.WriteLine(); Console.WriteLine($"Video needs converting. Continue? [y/n]"); if (Console.ReadKey().KeyChar == 'n') { return; } if (encoderConfiguration.CanUseCuda) { Console.Clear(); Console.WriteLine($"Use CUDA? [y/n]"); if (Console.ReadKey().KeyChar == 'n') { useCuda = false;; } } flags |= EncodingTargetFlags.NeedsNewVideo; } } catch (ArgumentException ex) { Console.Clear(); Console.WriteLine("Incompatible video: " + ex.Message); Console.ReadKey(); return; } try { if (!HasAcceptableAudio(dc, mediaInfo)) { Console.Clear(); Console.WriteLine($"Audio ({mediaInfo.AudioCodec}, (Accepted: {string.Join(", ", dc.AcceptedAudioCodecs)})) needs converting. Continue? [y/n]"); if (Console.ReadKey().KeyChar == 'n') { return; } flags |= EncodingTargetFlags.NeedsNewAudio; } } catch (ArgumentException ex) { Console.Clear(); Console.WriteLine("Incompatible audio: " + ex.Message); Console.ReadKey(); return; } if (flags == EncodingTargetFlags.None) { Console.Clear(); Console.WriteLine($"Video file {videoFile} doesn't need transcoding, adding to files to move."); Console.ReadKey(); return; } Console.Clear(); Console.WriteLine("Converting..."); encoderTranscodingInstance.OnSuccess += (object sender, EncodingSuccessEventArgs e) => { done = true; }; encoderTranscodingInstance.OnProgress += (object sender, EncodingProgressEventArgs e) => { Console.Clear(); Console.WriteLine($"Transcoding progress: {e.ProgressPercent:0.##}%"); }; // non-blocking, only starts the external engine await encoderTranscodingInstance.StartTranscodingAsync(new TranscodingJob { SourceFile = videoFile, Action = flags, Duration = mediaInfo.Duration, Subtitles = subtitles, UseCuda = useCuda }); while (!done) { // wait until encoding finished } Console.Clear(); Console.WriteLine("Done!"); Thread.Sleep(3 * 1000); } }