public void PrepareLiveChat(IJobDetail chatDownloadJob, long streamId) { var schedulerFactory = new StdSchedulerFactory(QuartzSchedulers.RamScheduler()); IScheduler scheduler = schedulerFactory.GetScheduler().Result; scheduler.Start(); ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("LiveStreamDownloadTrigger" + streamId) .StartNow() .Build(); scheduler.ScheduleJob(chatDownloadJob, trigger); }
public DeleteStreamReturn DeleteSingleStreamLogic(long streamId) { using (var context = new MainDataContext()) { var stream = context.Streams.FirstOrDefault(item => item.streamId == streamId); if (stream == null) { stream = context.Streams.FirstOrDefault(item => item.vodId == streamId); // add live stream delete capabilities } if (stream != null) { if (stream.downloadJobId != null) { var splitJobKey = stream.downloadJobId.Split("."); try { JobHelpers.CancelJob(splitJobKey[1], splitJobKey[0], QuartzSchedulers.PrimaryScheduler(), true); } catch (MissingJobException e) { _logger.Info(e.Message); } } if (stream.vodId != 0) { try { CleanUpStreamFiles(GlobalConfig.GetGlobalConfig("contentRootPath"), stream.vodId, stream.streamerId); } catch (DirectoryNotFoundException) { CleanUpStreamFiles(GlobalConfig.GetGlobalConfig("contentRootPath"), stream.streamId, stream.streamerId); } } else { CleanUpStreamFiles(GlobalConfig.GetGlobalConfig("contentRootPath"), stream.streamId, stream.streamerId); } context.Remove(stream); if (stream.chatDownloadJobId != null) { var splitJobKey = stream.chatDownloadJobId.Split("."); try { JobHelpers.CancelJob(splitJobKey[1], splitJobKey[0], QuartzSchedulers.PrimaryScheduler(), true); } catch (MissingJobException e) { _logger.Info(e.Message); } } using (var chatContext = new ChatDataContext()) { chatContext.Chats.RemoveRange(chatContext.Chats.Where(item => item.streamId == streamId)); chatContext.SaveChanges(); } } context.SaveChanges(); } TwitchApiHelpers twitchApiHelpers = new TwitchApiHelpers(); var request = twitchApiHelpers.TwitchRequest("https://api.twitch.tv/helix/videos?id=" + streamId, Method.GET); if (request.StatusCode == HttpStatusCode.OK) { return(new DeleteStreamReturn { isStillAvailable = true }); } return(new DeleteStreamReturn { isStillAvailable = false }); }
public Streamer CreateStreamerLogic(Streamer body) { Streamer returnStreamer; using (var context = new MainDataContext()) { Streamer streamer = context.Streamers.FirstOrDefault(item => item.streamerId == body.streamerId); if (streamer == null) { // if streamer does not exist in database and want to add _logger.Info("Adding new streamer..."); string etag = ""; if (GlobalConfig.GetGlobalConfig("contentRootPath") != null) { CreateFolder($"{GlobalConfig.GetGlobalConfig("contentRootPath")}/streamers/{body.streamerId}/"); if (!string.IsNullOrEmpty(body.thumbnailLocation)) { etag = DownloadHelpers.DownloadFile(body.thumbnailLocation, $"{GlobalConfig.GetGlobalConfig("contentRootPath")}/streamers/{body.streamerId}/thumbnail.png"); } } streamer = new Streamer { streamerId = body.streamerId, displayName = body.displayName, username = body.username, isLive = body.isLive ?? false, quality = body.quality == "{\"resolution\":0,\"fps\":0}" ? null : body.quality, getLive = body.getLive ?? false, thumbnailLocation = $"streamers/{body.streamerId}/thumbnail.png", thumbnailETag = etag }; context.Streamers.Add(streamer); returnStreamer = streamer; } /*else if (streamer != null) { * // if streamer exists then update * Console.WriteLine("Updating streamer..."); * streamer.streamerId = body.streamerId; * streamer.displayName = body.displayName; * streamer.username = body.username; * streamer.description = body.description; * streamer.viewCount = body.viewCount; * streamer.thumbnailLocation = $"streamers/{body.streamerId}/thumbnail.png"; * returnStreamer = streamer; * * IList<Parameter> headers = downloadHelpers.GetHeaders(body.thumbnailLocation); * for (var x = 0; x < headers.Count; x++) { * if (headers[x].Name == "ETag") { * var etag = headers[x].Value; * if (etag != null) { * if (streamer.thumbnailETag != etag.ToString().Replace("\"", "")) { * if (contentRootPath != null) * Console.WriteLine("Detected new thumbnail image, downloading..."); * streamer.thumbnailETag = downloadHelpers.DownloadFile(body.thumbnailLocation, * $"{contentRootPath.value}/streamers/{body.streamerId}/thumbnail.png"); * } * } * } * } * }*/ else { //something strange has happened returnStreamer = new Streamer(); } //if (isNew) { //StartupJobs startupJobs = new StartupJobs(); List <Streamer> streamers = new List <Streamer> { streamer }; //lazy JobHelpers.NormalJob <CheckForStreamerLiveStatusJob>("CreateStreamerUpdateLiveStatusJob", "CreateStreamerUpdateLiveStatusTrigger", QuartzSchedulers.PrimaryScheduler()); IJobDetail job = JobBuilder.Create <UpdateStreamerDetailsJob>() .WithIdentity("UpdateStreamerDetailsJob") .Build(); job.JobDataMap.Put("listOfStreamers", streamers); var schedulerFactory = new StdSchedulerFactory(QuartzSchedulers.PrimaryScheduler()); IScheduler scheduler = schedulerFactory.GetScheduler().Result; scheduler.Start(); ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("UpdateStreamerDetailsTrigger") .StartNow() .Build(); scheduler.ScheduleJob(job, trigger); //} context.SaveChanges(); } return(returnStreamer); }
public static void SetDownloadToFinished(long streamId, bool isLive) { Logger _logger = new NLog.LogFactory().GetCurrentClassLogger(); using (var context = new MainDataContext()) { Stream dbStream; dbStream = context.Streams.FirstOrDefault(item => item.streamId == streamId); string streamFile = GlobalConfig.GetGlobalConfig("contentRootPath") + dbStream.location + dbStream.fileName; dbStream.size = new FileInfo(streamFile).Length; dbStream.downloading = false; NotificationHub.Current.Clients.All.SendAsync($"{streamId}-completed", dbStream); if (isLive) { _logger.Info("Stopping live chat download..."); if (dbStream.chatDownloadJobId.Contains(".")) { var splitJobKey = dbStream.chatDownloadJobId.Split("."); JobHelpers.CancelJob(splitJobKey[1], splitJobKey[0], QuartzSchedulers.PrimaryScheduler()); } else { JobHelpers.CancelJob(dbStream.chatDownloadJobId, null, QuartzSchedulers.PrimaryScheduler()); } dbStream.chatDownloading = false; dbStream.duration = getStreamDuration(streamFile); GenerateThumbnailDuration(streamId); } else { _logger.Info("Stopping VOD chat download."); } context.SaveChanges(); // make another background job for this string checkVideoThumbnailsEnabled = GlobalConfig.GetGlobalConfig("generateVideoThumbnails"); if (checkVideoThumbnailsEnabled != null && checkVideoThumbnailsEnabled == "True") { _logger.Info("Queueing video thumbnail creation job..."); IJobDetail job = JobBuilder.Create <GenerateVideoThumbnailJob>() .WithIdentity("GenerateVideoThumbnail" + streamId) .UsingJobData("streamId", streamId) .UsingJobData("streamFile", streamFile) .Build(); var schedulerFactory = new StdSchedulerFactory(QuartzSchedulers.PrimaryScheduler()); IScheduler scheduler = schedulerFactory.GetScheduler().Result; scheduler.Start(); ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("GenerateVideoThumbnail" + streamId) .StartNow() .Build(); scheduler.ScheduleJob(job, trigger); //BackgroundJob.Enqueue(() => GenerateVideoThumbnail(streamId, streamFile)); } } }
public bool PrepareDownload(StreamExtended stream) { string streamUrl; streamUrl = "https://www.twitch.tv/videos/" + stream.streamId; YoutubeDlVideoJson.YoutubeDlVideoInfo youtubeDlVideoInfo = StreamHelpers.GetDownloadQualityUrl(streamUrl, stream.streamerId); string streamDirectory = $"{GlobalConfig.GetGlobalConfig("contentRootPath")}streamers/{stream.streamerId}/vods/{stream.streamId}"; Directory.CreateDirectory(streamDirectory); if (!string.IsNullOrEmpty(stream.thumbnailLocation)) { //todo handle missing thumbnail, maybe use youtubedl generated thumbnail instead DownloadHelpers.DownloadFile( stream.thumbnailLocation.Replace("%{width}", "320").Replace("%{height}", "180"), $"{streamDirectory}/thumbnail.jpg"); } string title = String.IsNullOrEmpty(stream.title) ? "vod" : stream.title; string outputPath = $"{streamDirectory}/{title}.{stream.streamId}"; string dbOutputPath = $"streamers/{stream.streamerId}/vods/{stream.streamId}/{title}.{stream.streamId}.mp4"; //TODO more should be queued, not done immediately IJobDetail job; string triggerIdentity; job = JobBuilder.Create <DownloadStreamJob>() .WithIdentity("StreamDownload" + stream.streamId) .UsingJobData("title", title) .UsingJobData("streamDirectory", streamDirectory) .UsingJobData("formatId", youtubeDlVideoInfo.formatId) .UsingJobData("url", streamUrl) .UsingJobData("isLive", false) .UsingJobData("youtubeDlVideoInfoDuration", youtubeDlVideoInfo.duration) .UsingJobData("retry", true) .RequestRecovery() .Build(); job.JobDataMap.Put("stream", stream); triggerIdentity = $"StreamDownload{stream.streamId}"; /*string jobId = BackgroundJob.Enqueue(() => * DownloadStream(stream, title, streamDirectory, youtubeDlVideoInfo.url, CancellationToken.None, * isLive, youtubeDlVideoInfo.duration));*/ Stream? dbStream; bool downloadChat = false; IJobDetail chatDownloadJob = new JobDetailImpl(); using (var context = new MainDataContext()) { dbStream = context.Streams.FirstOrDefault(item => item.streamId == stream.streamId); if (dbStream != null) { dbStream.streamId = stream.streamId; dbStream.streamerId = stream.streamerId; dbStream.quality = youtubeDlVideoInfo.quality; dbStream.url = youtubeDlVideoInfo.url; dbStream.title = stream.title; dbStream.createdAt = stream.createdAt; dbStream.location = $"streamers/{stream.streamerId}/vods/{stream.streamId}/"; dbStream.fileName = $"{title}.{stream.streamId}.mp4"; dbStream.duration = youtubeDlVideoInfo.duration; dbStream.downloading = true; dbStream.downloadJobId = job.Key.ToString(); } else { downloadChat = true; chatDownloadJob = JobBuilder.Create <ChatDownloadJob>() .WithIdentity("DownloadChat" + stream.streamId) .UsingJobData("streamId", stream.streamId) .UsingJobData("retry", true) .RequestRecovery() .Build(); dbStream = new Stream { streamId = stream.streamId, streamerId = stream.streamerId, quality = youtubeDlVideoInfo.quality, title = stream.title, url = youtubeDlVideoInfo.url, createdAt = stream.createdAt, location = $"streamers/{stream.streamerId}/vods/{stream.streamId}/", fileName = $"{title}.{stream.streamId}.mp4", duration = youtubeDlVideoInfo.duration, downloading = true, chatDownloading = true, downloadJobId = job.Key.ToString(), chatDownloadJobId = chatDownloadJob.Key.ToString() }; // only download chat if this is a new vod context.Add(dbStream); } context.SaveChanges(); } //var chatSchedulerFactory = new StdSchedulerFactory(QuartzSchedulers.SingleThreadScheduler()); var vodSchedulerFactory = new StdSchedulerFactory(QuartzSchedulers.PrimaryScheduler()); //IScheduler chatScheduler = chatSchedulerFactory.GetScheduler().Result; IScheduler vodScheduler = vodSchedulerFactory.GetScheduler().Result; //chatScheduler.Start(); vodScheduler.Start(); ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity(triggerIdentity) .StartNow() .Build(); vodScheduler.ScheduleJob(job, trigger); if (downloadChat) { ISimpleTrigger chatDownloadTrigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("DownloadChat" + stream.streamId) .StartNow() .Build(); vodScheduler.ScheduleJob(chatDownloadJob, chatDownloadTrigger); } //_hubContext.Clients.All.SendAsync("ReceiveMessage", CheckForDownloadingStreams()); return(true); }
public Task PrepareLiveStreamDownload(StreamExtended stream, string streamerName) { streamUrl = "https://twitch.tv/" + streamerName; YoutubeDlVideoJson.YoutubeDlVideoInfo youtubeDlVideoInfo = StreamHelpers.GetDownloadQualityUrl(streamUrl, stream.streamerId); streamDirectory = $"{GlobalConfig.GetGlobalConfig("contentRootPath")}streamers/{stream.streamerId}/vods/{stream.streamId}"; try { Directory.CreateDirectory(streamDirectory); } catch (UnauthorizedAccessException e) { _logger.Error(e); // todo handle this throw; } outputPath = $"{streamDirectory}/{stream.title}.{stream.streamId}"; dbOutputPath = $"streamers/{stream.streamerId}/vods/{stream.streamId}/{stream.title}.{stream.streamId}.mp4"; var job = JobBuilder.Create <LiveStreamDownloadJob>() .WithIdentity("LiveStreamDownloadJob" + stream.streamId) .UsingJobData("url", streamUrl) .UsingJobData("streamDirectory", streamDirectory) .UsingJobData("streamId", stream.streamId) .UsingJobData("title", stream.title) .UsingJobData("streamerId", stream.streamerId) .Build(); var triggerIdentity = $"LiveStreamDownload{stream.streamId}"; var chatDownloadJob = JobBuilder.Create <LiveStreamChatDownloadJob>() .WithIdentity("LiveStreamChatDownloadJob" + stream.streamId) .UsingJobData("channel", streamerName) .UsingJobData("streamId", stream.streamId) .Build(); using (var context = new MainDataContext()) { var dbStream = new Stream { streamId = stream.streamId, vodId = stream.vodId, streamerId = stream.streamerId, quality = youtubeDlVideoInfo.quality, title = stream.title, url = youtubeDlVideoInfo.url, createdAt = stream.createdAt, location = $"streamers/{stream.streamerId}/vods/{stream.streamId}/", fileName = $"{stream.title}.{stream.streamId}.mp4", downloading = true, chatDownloading = true, downloadJobId = job.Key.ToString(), chatDownloadJobId = chatDownloadJob.Key.ToString() }; context.Add(dbStream); context.SaveChanges(); } var schedulerFactory = new StdSchedulerFactory(QuartzSchedulers.RamScheduler()); IScheduler scheduler = schedulerFactory.GetScheduler().Result; scheduler.Start(); ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity(triggerIdentity) .StartNow() .Build(); scheduler.ScheduleJob(job, trigger); PrepareLiveChat(chatDownloadJob, stream.streamId); return(Task.CompletedTask); }