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);
        }
Ejemplo n.º 2
0
        public static YoutubeDlVideoJson.YoutubeDlVideoInfo ParseBestPossibleQuality(
            YoutubeDlVideoJson.YoutubeDlVideo deserializedJson, int streamerId)
        {
            var returnValue = new YoutubeDlVideoJson.YoutubeDlVideoInfo();

            List <SetupQualityExtendedJsonClass> availableQualities = new List <SetupQualityExtendedJsonClass>();

            for (var x = 0; x < deserializedJson.formats.Count; x++)
            {
                availableQualities.Add(new SetupQualityExtendedJsonClass {
                    Resolution = deserializedJson.formats[x].height,
                    Fps        = RoundToNearest10(Convert.ToInt32(deserializedJson.formats[x].fps)),
                    tbr        = deserializedJson.formats[x].tbr.Value
                });
            }

            // sort by highest quality first
            availableQualities = availableQualities.OrderByDescending(item => item.tbr).ToList();


            // saving for later, just in case

            /*for (var x = 0; x < deserializedJson.formats.Count; x++) {
             *  var splitRes = deserializedJson.formats[x].format_id.Split("p");
             *  availableQualities.Add(new SetupQualityExtendedJsonClass() {
             *      Resolution = int.Parse(splitRes[0]),
             *      Fps = int.Parse(splitRes[1]),
             *      Counter = x
             *  });
             * }*/

            Streamer streamerQuality;
            string   defaultQuality = GlobalConfig.GetGlobalConfig("streamQuality");

            using (var context = new MainDataContext()) {
                streamerQuality = context.Streamers.FirstOrDefault(item => item.streamerId == streamerId);
            }

            int    resolution = 0;
            double fps        = 0;

            if (streamerQuality != null && streamerQuality.quality == null)
            {
                if (defaultQuality != null)
                {
                    var parsedQuality = JsonConvert.DeserializeObject <SetupQualityJsonClass>(defaultQuality);

                    resolution = parsedQuality.Resolution;
                    fps        = parsedQuality.Fps;
                }
            }
            else
            {
                var parsedQuality = JsonConvert.DeserializeObject <SetupQualityJsonClass>(streamerQuality.quality);

                resolution = parsedQuality.Resolution;
                fps        = parsedQuality.Fps;
            }


            if (resolution != 0 && fps != 0)
            {
                // check if the chosen resolution and fps is available
                var existingQuality =
                    availableQualities.FirstOrDefault(item => item.Resolution == resolution && item.Fps == fps);

                if (existingQuality != null)
                {
                    var selectedQuality =
                        deserializedJson.formats.FirstOrDefault(item => item.tbr == existingQuality.tbr);
                    if (selectedQuality != null)
                    {
                        returnValue.url     = selectedQuality.url;
                        returnValue.quality = selectedQuality.height;
                    }
                }
                else
                {
                    // get same resolution, but different fps (720p 60fps not available, maybe 720p 30fps?)
                    existingQuality = availableQualities.FirstOrDefault(item => item.Resolution == resolution);
                    if (existingQuality != null)
                    {
                        var selectedQuality =
                            deserializedJson.formats.FirstOrDefault(item => item.tbr == existingQuality.tbr);
                        if (selectedQuality != null)
                        {
                            returnValue.url     = selectedQuality.url;
                            returnValue.quality = selectedQuality.height;
                        }
                    }
                    else
                    {
                        // same resolution and fps not available; choose the next best value (after sorting the list)
                        existingQuality = availableQualities.FirstOrDefault(item => item.Resolution < resolution);

                        if (existingQuality != null)
                        {
                            var selectedQuality =
                                deserializedJson.formats.FirstOrDefault(item => item.tbr == existingQuality.tbr);
                            if (selectedQuality != null)
                            {
                                returnValue.url     = selectedQuality.url;
                                returnValue.quality = selectedQuality.height;
                            }
                        }
                    }
                }
            }
            else
            {
                returnValue.url     = deserializedJson.url;
                returnValue.quality = deserializedJson.height;
            }

            return(returnValue);
        }
Ejemplo n.º 3
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);
        }