public Task DownloadStream(StreamExtended stream, string title, string streamDirectory, string formatId, string url, long duration, CancellationToken cancellationToken) { StreamDownload streamDownload = new StreamDownload(new DirectoryInfo(streamDirectory), false); _logger.Info("Getting vod m3u8.."); streamDownload.GetVodM3U8(url); _logger.Info("Got vod m3u8."); try { _logger.Info("Getting vod parts..."); streamDownload.GetVodParts(cancellationToken); _logger.Info("Got vod parts."); } catch (TsFileNotFound e) { // test if the stream has gone down. try { streamDownload.GetVodM3U8(url); } catch (Exception exception) { if (!exception.Message.Contains("is offline")) { // stream is offline, must have finished, so is not an error. // stream has not finished, throw _logger.Error($"Error occured while downloading a live stream. {exception.Message}"); Streamer streamer; using (var mainDataContext = new MainDataContext()) { streamer = mainDataContext.Streamers.FirstOrDefault(item => item.streamerId == stream.streamerId); } if (streamer != null) { NotificationLogic.CreateNotification($"StreamDownloadJob{stream.streamId}", Severity.Error, Position.Top, $"Could not download VOD for {streamer.username}.", $"/streamer/{streamer.id}"); } streamDownload.CleanUpFiles(); throw; } } } _logger.Info("Combining ts files..."); streamDownload.CombineTsFiles(title, stream.streamId); _logger.Info("Combined ts files."); _logger.Info("Cleaning up files..."); streamDownload.CleanUpFiles(); _logger.Info("Cleaned up files."); File.Move($"{streamDownload._rootDirectory.FullName}/stream.mp4", $"{streamDownload._rootDirectory.FullName}/{title}.{stream.streamId}.mp4"); _logger.Info("Moved file."); StreamHelpers.SetDownloadToFinished(stream.streamId, false); return(Task.CompletedTask); //await _hubContext.Clients.All.SendAsync("ReceiveMessage", CheckForDownloadingStreams()); }
public void UpdateLiveStatus(List <Streamer> listOfStreamers) { string listOfIds = "?user_id="; for (int i = 0; i < listOfStreamers.Count; i++) { if (i != listOfStreamers.Count - 1) { listOfIds += listOfStreamers[i].streamerId + "&user_id="; } else { listOfIds += listOfStreamers[i].streamerId; } } TwitchApiHelpers twitchApiHelpers = new TwitchApiHelpers(); var response = twitchApiHelpers.TwitchRequest($"https://api.twitch.tv/helix/streams{listOfIds}&first=100", Method.GET); StreamHelpers.GetStreamsResult liveStream = JsonConvert.DeserializeObject <StreamHelpers.GetStreamsResult>(response.Content); for (int x = 0; x < listOfStreamers.Count; x++) { var stream = liveStream.data.FirstOrDefault(item => item.user_id == listOfStreamers[x].streamerId); if (stream != null && stream.type == "live") { // if live and if not a re-run or something else using (var context = new MainDataContext()) { var alreadyExistingStream = context.Streams.FirstOrDefault(item => item.vodId == Int64.Parse(stream.id)); var streamer = context.Streamers.FirstOrDefault(item => item.streamerId == listOfStreamers[x].streamerId); if (streamer.isLive == false) { streamer.isLive = true; context.SaveChanges(); } NotificationHub.Current.Clients.All.SendAsync($"{streamer.id}Live", true); if (streamer.getLive == false || alreadyExistingStream != null) { // already downloading/downloaded, or user does not want to download this streamers live stream continue; } } if (DateTime.UtcNow.Subtract(stream.started_at).TotalMinutes < 5) { // queue up the stream to be downloaded StreamExtended convertedLiveStream = new StreamExtended { streamId = StreamHelpers.GetStreamDetails(Int64.Parse(stream.id), true, stream.user_id).streamId, vodId = Int64.Parse(stream.id), streamerId = stream.user_id, title = stream.title, createdAt = stream.started_at }; CreateLiveStream createLiveStream = new CreateLiveStream(); createLiveStream.PrepareLiveStreamDownload(convertedLiveStream, stream.user_login); } } else { using (var context = new MainDataContext()) { var streamer = context.Streamers.FirstOrDefault(item => item.streamerId == listOfStreamers[x].streamerId); if (streamer.isLive == true) { streamer.isLive = false; context.SaveChanges(); } NotificationHub.Current.Clients.All.SendAsync($"{streamer.id}Live", false); } } } }
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); }