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());
        }
예제 #2
0
        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);
        }
예제 #4
0
        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);
        }