Beispiel #1
0
        public override async Task <(ResultType result, List <DownloadInfo> downloadInfos)> GetFileUrlsOfVod(CancellationToken cancellationToken)
        {
            (bool retrieveVideoSuccess, HitboxVideo video) = await Hitbox.RetrieveVideo(VideoInfo.VideoId);

            if (!retrieveVideoSuccess)
            {
                return(ResultType.Failure, null);
            }

            VideoInfo = new HitboxVideoInfo(video);

            // TODO: Figure out how to determine quality when there are multiple.
            string m3u8path   = "https://smashcast-vod.akamaized.net/static/videos/vods" + GetM3U8PathFromM3U(video.MediaProfiles.First().Url);
            string folderpath = TsVideoJob.GetFolder(m3u8path);
            string m3u8;

            HttpClient          client   = new HttpClient();
            HttpResponseMessage response = await client.GetAsync(new Uri( m3u8path ));

            if (response.StatusCode == System.Net.HttpStatusCode.OK)
            {
                m3u8 = await response.Content.ReadAsStringAsync();
            }
            else
            {
                return(ResultType.Failure, null);
            }

            // who cares, hitbox is dead
            return(ResultType.Failure, null);

            //string[] filenames = TsVideoJob.GetFilenamesFromM3U8( m3u8 );
            //List<string> urls = new List<string>( filenames.Length );
            //foreach ( var filename in filenames ) {
            //	urls.Add( folderpath + filename );
            //}
            //return (ResultType.Success, urls.ToArray());
        }
Beispiel #2
0
        public override async Task <(ResultType result, List <DownloadInfo> downloadInfos)> GetFileUrlsOfVod(CancellationToken cancellationToken)
        {
            var video_json = await TwitchYTDL.GetVideoJson(long.Parse(VideoInfo.VideoId));

            VideoInfo = new TwitchVideoInfo(TwitchYTDL.VideoFromJson(video_json));

            string folderpath;
            List <DownloadInfo> downloadInfos;

            while (true)
            {
                try {
                    bool interactive = false;
                    if (interactive)
                    {
                        Status = "";
                        string tmp1         = Path.Combine(GetTempFolder(), GetTempFilenameWithoutExtension() + "_baseurl.txt");
                        string tmp2         = Path.Combine(GetTempFolder(), GetTempFilenameWithoutExtension() + "_tsnames.txt");
                        string linesbaseurl = TryGetUserCopyBaseurlM3U(tmp1);
                        string linestsnames = TryGetUserCopyTsnamesM3U(tmp2);
                        if (linesbaseurl == null)
                        {
                            File.WriteAllText(tmp1, "get baseurl for m3u8 from https://www.twitch.tv/videos/" + VideoInfo.VideoId);
                        }
                        if (linestsnames == null)
                        {
                            File.WriteAllText(tmp2, "get actual m3u file from https://www.twitch.tv/videos/" + VideoInfo.VideoId);
                        }

                        if (linesbaseurl == null || linestsnames == null)
                        {
                            await Task.Delay(200);

                            Process.Start(tmp1);
                            await Task.Delay(200);

                            Process.Start(tmp2);
                            await Task.Delay(200);

                            return(ResultType.UserInputRequired, null);
                        }

                        folderpath    = TsVideoJob.GetFolder(GetM3U8PathFromM3U(linesbaseurl, VideoQuality));
                        downloadInfos = TsVideoJob.GetFilenamesFromM3U8(folderpath, linestsnames);
                    }
                    else
                    {
                        string m3u8path = ExtractM3u8FromJson(video_json);
                        folderpath = TsVideoJob.GetFolder(m3u8path);
                        var client = new System.Net.Http.HttpClient();
                        var result = await client.GetAsync(m3u8path);

                        string m3u8 = await result.Content.ReadAsStringAsync();

                        downloadInfos = TsVideoJob.GetFilenamesFromM3U8(folderpath, m3u8);
                    }
                } catch (TwitchHttpException e) {
                    if (e.StatusCode == System.Net.HttpStatusCode.NotFound && VideoInfo.VideoRecordingState == RecordingState.Live)
                    {
                        // this can happen on streams that have just started, in this just wait a bit and retry
                        try {
                            await Task.Delay(20000, cancellationToken);
                        } catch (TaskCanceledException) {
                            return(ResultType.Cancelled, null);
                        }
                        video_json = await TwitchYTDL.GetVideoJson(long.Parse(VideoInfo.VideoId));

                        VideoInfo = new TwitchVideoInfo(TwitchYTDL.VideoFromJson(video_json));
                        continue;
                    }
                    else
                    {
                        throw;
                    }
                }
                break;
            }

            return(ResultType.Success, downloadInfos);
        }
Beispiel #3
0
 public UserInputRequestStreamLive(TsVideoJob job)
 {
     Job = job;
 }
Beispiel #4
0
 public UserInputRequestTimeMismatchRemuxed(TsVideoJob job)
 {
     Job = job;
 }
Beispiel #5
0
 public UserInputRequestTimeMismatchCombined(TsVideoJob job)
 {
     Job = job;
 }
Beispiel #6
0
        public override async Task <ResultType> Run(CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return(ResultType.Cancelled);
            }

            JobStatus = VideoJobStatus.Running;
            Status    = "Retrieving video info...";
            (ResultType getFileUrlsResult, List <DownloadInfo> downloadInfos) = await GetFileUrlsOfVod(cancellationToken);

            if (getFileUrlsResult == ResultType.UserInputRequired)
            {
                Status = "Need manual fetch of file URLs.";
                return(ResultType.UserInputRequired);
            }
            if (getFileUrlsResult != ResultType.Success)
            {
                Status = "Failed retrieving file URLs.";
                return(ResultType.Failure);
            }

            string combinedTempname = Path.Combine(GetTempFolder(), GetTempFilenameWithoutExtension() + "_combined.ts");
            string combinedFilename = Path.Combine(GetTempFolder(), GetFinalFilenameWithoutExtension() + ".ts");
            string remuxedTempname  = Path.Combine(GetTempFolder(), GetTempFilenameWithoutExtension() + "_combined.mp4");
            string remuxedFilename  = Path.Combine(GetTempFolder(), GetFinalFilenameWithoutExtension() + ".mp4");
            string targetFilename   = Path.Combine(GetTargetFolder(), GetFinalFilenameWithoutExtension() + ".mp4");
            string baseurlfilepath  = Path.Combine(GetTempFolder(), GetTempFilenameWithoutExtension() + "_baseurl.txt");
            string tsnamesfilepath  = Path.Combine(GetTempFolder(), GetTempFilenameWithoutExtension() + "_tsnames.txt");

            if (!await Util.FileExists(targetFilename))
            {
                if (!await Util.FileExists(combinedFilename))
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        return(ResultType.Cancelled);
                    }

                    Status = "Downloading files...";
                    string[] files;
                    while (true)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            return(ResultType.Cancelled);
                        }

                        System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
                        timer.Start();
                        var downloadResult = await Download(this, cancellationToken, GetTempFolderForParts(), downloadInfos);

                        if (downloadResult.result != ResultType.Success)
                        {
                            return(downloadResult.result);
                        }
                        files = downloadResult.files;
                        if (this.AssumeFinished || this.VideoInfo.VideoRecordingState != RecordingState.Live)
                        {
                            break;
                        }
                        else
                        {
                            // we're downloading a stream that is still streaming
                            timer.Stop();
                            // if too little time has passed wait a bit to allow the stream to provide new data
                            if (timer.Elapsed.TotalMinutes < 2.5)
                            {
                                TimeSpan ts = TimeSpan.FromMinutes(2.5) - timer.Elapsed;
                                Status            = "Waiting " + ts.TotalSeconds + " seconds for stream to update...";
                                _UserInputRequest = new UserInputRequestStreamLive(this);
                                try {
                                    await Task.Delay(ts, cancellationToken);
                                } catch (TaskCanceledException) {
                                    return(ResultType.Cancelled);
                                }
                            }
                            if (File.Exists(tsnamesfilepath))
                            {
                                File.Delete(tsnamesfilepath);
                            }
                            (getFileUrlsResult, downloadInfos) = await GetFileUrlsOfVod(cancellationToken);

                            if (getFileUrlsResult != ResultType.Success)
                            {
                                Status = "Failed retrieving file URLs.";
                                return(ResultType.Failure);
                            }
                        }
                    }
                    _UserInputRequest = null;

                    Status = "Waiting for free disk IO slot to combine...";
                    try {
                        await Util.ExpensiveDiskIOSemaphore.WaitAsync(cancellationToken);
                    } catch (OperationCanceledException) {
                        return(ResultType.Cancelled);
                    }
                    try {
                        long expectedTargetFilesize = 0;
                        foreach (var file in files)
                        {
                            expectedTargetFilesize += new FileInfo(file).Length;
                        }

                        Status = "Combining downloaded video parts...";
                        if (await Util.FileExists(combinedTempname))
                        {
                            await Util.DeleteFile(combinedTempname);
                        }
                        await StallWrite(combinedFilename, expectedTargetFilesize, cancellationToken);

                        if (cancellationToken.IsCancellationRequested)
                        {
                            return(ResultType.Cancelled);
                        }
                        ResultType combineResult = await TsVideoJob.Combine(cancellationToken, combinedTempname, files);

                        if (combineResult != ResultType.Success)
                        {
                            return(combineResult);
                        }

                        // sanity check
                        Status = "Sanity check on combined video...";
                        TimeSpan actualVideoLength   = (await FFMpegUtil.Probe(combinedTempname)).Duration;
                        TimeSpan expectedVideoLength = VideoInfo.VideoLength;
                        if (!IgnoreTimeDifferenceCombined && actualVideoLength.Subtract(expectedVideoLength).Duration() > TimeSpan.FromSeconds(5))
                        {
                            // if difference is bigger than 5 seconds something is off, report
                            Status                 = "Large time difference between expected (" + expectedVideoLength.ToString() + ") and combined (" + actualVideoLength.ToString() + "), stopping.";
                            _UserInputRequest      = new UserInputRequestTimeMismatchCombined(this);
                            _IsWaitingForUserInput = true;
                            return(ResultType.UserInputRequired);
                        }

                        Util.MoveFileOverwrite(combinedTempname, combinedFilename);
                        await Util.DeleteFiles(files);

                        System.IO.Directory.Delete(GetTempFolderForParts());
                    } finally {
                        Util.ExpensiveDiskIOSemaphore.Release();
                    }
                }

                Status = "Waiting for free disk IO slot to remux...";
                try {
                    await Util.ExpensiveDiskIOSemaphore.WaitAsync(cancellationToken);
                } catch (OperationCanceledException) {
                    return(ResultType.Cancelled);
                }
                try {
                    Status = "Remuxing to MP4...";
                    if (await Util.FileExists(remuxedTempname))
                    {
                        await Util.DeleteFile(remuxedTempname);
                    }
                    await StallWrite(remuxedFilename, new FileInfo( combinedFilename ).Length, cancellationToken);

                    if (cancellationToken.IsCancellationRequested)
                    {
                        return(ResultType.Cancelled);
                    }
                    await Task.Run(() => TsVideoJob.Remux(remuxedFilename, combinedFilename, remuxedTempname));

                    // sanity check
                    Status = "Sanity check on remuxed video...";
                    TimeSpan actualVideoLength   = (await FFMpegUtil.Probe(remuxedFilename)).Duration;
                    TimeSpan expectedVideoLength = VideoInfo.VideoLength;
                    if (!IgnoreTimeDifferenceRemuxed && actualVideoLength.Subtract(expectedVideoLength).Duration() > TimeSpan.FromSeconds(5))
                    {
                        // if difference is bigger than 5 seconds something is off, report
                        Status                 = "Large time difference between expected (" + expectedVideoLength.ToString() + ") and remuxed (" + actualVideoLength.ToString() + "), stopping.";
                        _UserInputRequest      = new UserInputRequestTimeMismatchRemuxed(this);
                        _IsWaitingForUserInput = true;
                        return(ResultType.UserInputRequired);
                    }

                    Util.MoveFileOverwrite(remuxedFilename, targetFilename);
                } finally {
                    Util.ExpensiveDiskIOSemaphore.Release();
                }
            }

            Status    = "Done!";
            JobStatus = VideoJobStatus.Finished;
            if (File.Exists(tsnamesfilepath))
            {
                File.Delete(tsnamesfilepath);
            }
            if (File.Exists(baseurlfilepath))
            {
                File.Delete(baseurlfilepath);
            }
            return(ResultType.Success);
        }