예제 #1
0
        private void WriteNewPlaylist(Action <string> log, WebChunkList webChunkList, string playlistFile)
        {
            log(Environment.NewLine + Environment.NewLine + "Creating local m3u8 playlist for FFMPEG...");

            StringBuilder sb = new StringBuilder();

            webChunkList.Header.ForEach(line =>
            {
                sb.AppendLine(line);
            });

            webChunkList.Content.ForEach(webChunk =>
            {
                sb.AppendLine(webChunk.ExtInf);
                sb.AppendLine(webChunk.LocalFile);
            });

            webChunkList.Footer.ForEach(line =>
            {
                sb.AppendLine(line);
            });

            log(" done!");

            log(Environment.NewLine + "Writing playlist to '" + playlistFile + "'...");
            FileSystem.DeleteFile(playlistFile);
            File.WriteAllText(playlistFile, sb.ToString());
            log(" done!");
        }
예제 #2
0
        private void DownloadChunks(Action <string> log, Action <string> setStatus, Action <int> setProgress,
                                    WebChunkList webChunkList, CancellationToken cancellationToken)
        {
            int webChunkCount      = webChunkList.Content.Count;
            int maxConnectionCount = ServicePointManager.DefaultConnectionLimit;

            log(Environment.NewLine + Environment.NewLine + "Starting parallel video chunk download");
            log(Environment.NewLine + "Number of video chunks to download: " + webChunkCount);
            log(Environment.NewLine + "Maximum connection count: " + maxConnectionCount);

            setStatus("Downloading");

            log(Environment.NewLine + Environment.NewLine + "Parallel video chunk download is running...");

            long completedChunkDownloads = 0;

            Parallel.ForEach(webChunkList.Content, new ParallelOptions()
            {
                MaxDegreeOfParallelism = maxConnectionCount - 1
            }, (webChunk, loopState) =>
            {
                using (WebClient downloadClient = new WebClient())
                {
                    byte[] bytes = downloadClient.DownloadData(webChunk.DownloadUrl);

                    Interlocked.Increment(ref completedChunkDownloads);

                    FileSystem.DeleteFile(webChunk.LocalFile);

                    File.WriteAllBytes(webChunk.LocalFile, bytes);

                    long completed = Interlocked.Read(ref completedChunkDownloads);

                    setProgress((int)(completedChunkDownloads * 100 / webChunkCount));
                }

                if (cancellationToken.IsCancellationRequested)
                {
                    loopState.Stop();
                }
            });

            setProgress(100);

            log(" done!");

            log(Environment.NewLine + Environment.NewLine + "Download of all video chunks complete!");
        }
예제 #3
0
        private WebChunkList RetrieveWebChunkList(Action <string> log, WebClient webClient, string tempDir, string playlistUrl)
        {
            log(Environment.NewLine + Environment.NewLine + "Retrieving playlist...");
            string playlistStr = webClient.DownloadString(playlistUrl);

            log(" done!");

            string playlistUrlPrefix = playlistUrl.Substring(0, playlistUrl.LastIndexOf("/") + 1);

            log(Environment.NewLine + "Parsing playlist...");
            WebChunkList webChunkList = WebChunkList.Parse(tempDir, playlistStr, playlistUrlPrefix);

            log(" done!");

            log(Environment.NewLine + "Number of video chunks: " + webChunkList.Content.Count);

            return(webChunkList);
        }
예제 #4
0
        private CropInfo CropWebChunkList(WebChunkList webChunkList, bool cropStart, bool cropEnd, TimeSpan cropStartTime, TimeSpan cropEndTime)
        {
            double start     = cropStartTime.TotalMilliseconds;
            double lengthOrg = cropEndTime.TotalMilliseconds;
            double length    = cropEndTime.TotalMilliseconds;

            if (cropStart)
            {
                length -= start;
            }

            start     = Math.Round(start / 1000, 3);
            lengthOrg = Math.Round(lengthOrg / 1000, 3);
            length    = Math.Round(length / 1000, 3);

            List <WebChunk> content = webChunkList.Content;

            List <WebChunk> deleteStart = new List <WebChunk>();
            List <WebChunk> deleteEnd   = new List <WebChunk>();

            if (cropStart)
            {
                double chunkSum = 0;

                foreach (WebChunk webChunk in content)
                {
                    chunkSum += webChunk.Length;

                    if (chunkSum < start)
                    {
                        deleteStart.Add(webChunk);
                    }
                    else
                    {
                        start = Math.Round(chunkSum - start, 3);
                        break;
                    }
                }
            }

            if (cropEnd)
            {
                double chunkSum = 0;

                foreach (WebChunk webChunk in content)
                {
                    chunkSum += webChunk.Length;

                    if (chunkSum > lengthOrg)
                    {
                        deleteEnd.Add(webChunk);
                    }
                }
            }

            deleteStart.ForEach(webChunk =>
            {
                content.Remove(webChunk);
            });

            deleteEnd.ForEach(webChunk =>
            {
                content.Remove(webChunk);
            });

            return(new CropInfo(cropStart, cropEnd, cropStart ? start : 0, length));
        }
예제 #5
0
        private void StartQueuedDownloadIfExists()
        {
            if (this.paused)
            {
                return;
            }

            if (Monitor.TryEnter(this.changeDownloadLockObject))
            {
                try
                {
                    if (!this.downloads.Where(d => d.DownloadStatus == DownloadStatus.Active).Any())
                    {
                        TwitchVideoDownload download = this.downloads.Where(d => d.DownloadStatus == DownloadStatus.Queued).FirstOrDefault();

                        if (download == null)
                        {
                            return;
                        }

                        DownloadParameters downloadParams = download.DownloadParams;

                        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
                        CancellationToken       cancellationToken       = cancellationTokenSource.Token;

                        string downloadId   = download.Id;
                        string urlIdTrimmed = downloadParams.Video.IdTrimmed;
                        string tempDir      = Path.Combine(downloadParams.Folder, TEMP_PREFIX + downloadId);
                        string playlistFile = Path.Combine(tempDir, PLAYLIST_NAME);
                        string ffmpegFile   = Path.Combine(appDir, Environment.Is64BitOperatingSystem ? FFMPEG_EXE_X64 : FFMPEG_EXE_X86);
                        string outputFile   = downloadParams.FullPath;

                        bool cropStart = downloadParams.CropStart;
                        bool cropEnd   = downloadParams.CropEnd;

                        TimeSpan cropStartTime = downloadParams.CropStartTime;
                        TimeSpan cropEndTime   = downloadParams.CropEndTime;

                        TwitchVideoResolution resolution = downloadParams.Resolution;

                        Action <DownloadStatus> setDownloadStatus = download.SetDownloadStatus;
                        Action <string>         log           = download.AppendLog;
                        Action <string>         setStatus     = download.SetStatus;
                        Action <int>            setProgress   = download.SetProgress;
                        Action <bool>           setIsEncoding = download.SetIsEncoding;

                        Task downloadVideoTask = new Task(() =>
                        {
                            setStatus("Initializing");

                            log("Download task has been started!");

                            this.WriteDownloadInfo(log, downloadParams, ffmpegFile, tempDir);

                            this.CheckTempDirectory(log, tempDir);

                            using (WebClient webClient = new WebClient())
                            {
                                AuthInfo authInfo = this.RetrieveAuthInfo(log, webClient, urlIdTrimmed);

                                cancellationToken.ThrowIfCancellationRequested();

                                string playlistUrl = this.RetrievePlaylistUrlForQuality(log, webClient, resolution, urlIdTrimmed, authInfo);

                                cancellationToken.ThrowIfCancellationRequested();

                                WebChunkList webChunkList = this.RetrieveWebChunkList(log, webClient, tempDir, playlistUrl);

                                cancellationToken.ThrowIfCancellationRequested();

                                CropInfo cropInfo = this.CropWebChunkList(webChunkList, cropStart, cropEnd, cropStartTime, cropEndTime);

                                cancellationToken.ThrowIfCancellationRequested();

                                this.DownloadChunks(log, setStatus, setProgress, webChunkList, cancellationToken);

                                cancellationToken.ThrowIfCancellationRequested();

                                this.WriteNewPlaylist(log, webChunkList, playlistFile);

                                cancellationToken.ThrowIfCancellationRequested();

                                this.EncodeVideo(log, setStatus, setProgress, setIsEncoding, ffmpegFile, playlistFile, outputFile, cropInfo);
                            }
                        }, cancellationToken);

                        Task continueTask = downloadVideoTask.ContinueWith(task =>
                        {
                            log(Environment.NewLine + Environment.NewLine + "Starting temporary download folder cleanup!");
                            this.CleanUp(tempDir, log);

                            setProgress(100);
                            setIsEncoding(false);

                            bool success = false;

                            if (task.IsFaulted)
                            {
                                setDownloadStatus(DownloadStatus.Error);
                                log(Environment.NewLine + Environment.NewLine + "Download task ended with an error!");

                                if (task.Exception != null)
                                {
                                    log(Environment.NewLine + Environment.NewLine + task.Exception.ToString());
                                }
                            }
                            else if (task.IsCanceled)
                            {
                                setDownloadStatus(DownloadStatus.Canceled);
                                log(Environment.NewLine + Environment.NewLine + "Download task was canceled!");
                            }
                            else
                            {
                                success = true;
                                setDownloadStatus(DownloadStatus.Finished);
                                log(Environment.NewLine + Environment.NewLine + "Download task ended successfully!");
                            }

                            DownloadTask downloadTask;

                            if (!this.downloadTasks.TryRemove(downloadId, out downloadTask))
                            {
                                throw new ApplicationException("Could not remove download task with ID '" + downloadId + "' from download task collection!");
                            }

                            if (success && this.preferencesService.CurrentPreferences.DownloadRemoveCompleted)
                            {
                                this.eventAggregator.GetEvent <DownloadCompletedEvent>().Publish(downloadId);
                            }
                        });

                        if (this.downloadTasks.TryAdd(downloadId, new DownloadTask(downloadVideoTask, continueTask, cancellationTokenSource)))
                        {
                            downloadVideoTask.Start();
                            setDownloadStatus(DownloadStatus.Active);
                        }
                    }
                }
                finally
                {
                    Monitor.Exit(this.changeDownloadLockObject);
                }
            }
        }