3
3
using System ;
4
4
using System . Collections . Generic ;
5
5
using System . ComponentModel ;
6
+ using System . Diagnostics ;
6
7
using System . Drawing ;
7
8
using System . Globalization ;
8
9
using System . Linq ;
27
28
using Xabe . FFmpeg ;
28
29
using WpfAnimatedGif ;
29
30
using TwitchDownloader . Properties ;
31
+ using Xabe . FFmpeg . Events ;
30
32
31
33
namespace TwitchDownloaderWPF
32
34
{
@@ -55,7 +57,11 @@ private void SetEnabled(bool isEnabled)
55
57
numEndMinute . IsEnabled = isEnabled ;
56
58
numEndSecond . IsEnabled = isEnabled ;
57
59
btnDownload . IsEnabled = isEnabled ;
58
- numDownloadThreads . IsEnabled = isEnabled ;
60
+ }
61
+ private void Hyperlink_RequestNavigate ( object sender , RequestNavigateEventArgs e )
62
+ {
63
+ Process . Start ( new ProcessStartInfo ( e . Uri . AbsoluteUri ) ) ;
64
+ e . Handled = true ;
59
65
}
60
66
61
67
private async void btnGetInfo_Click ( object sender , RoutedEventArgs e )
@@ -67,7 +73,7 @@ private async void btnGetInfo_Click(object sender, RoutedEventArgs e)
67
73
try
68
74
{
69
75
Task < JObject > taskInfo = InfoHelper . GetVideoInfo ( videoId ) ;
70
- Task < JObject > taskAccessToken = InfoHelper . GetVideoToken ( videoId ) ;
76
+ Task < JObject > taskAccessToken = InfoHelper . GetVideoToken ( videoId , textOauth . Text ) ;
71
77
await Task . WhenAll ( taskInfo , taskAccessToken ) ;
72
78
string thumbUrl = taskInfo . Result [ "data" ] [ 0 ] [ "thumbnail_url" ] . ToString ( ) . Replace ( "%{width}" , 512 . ToString ( ) ) . Replace ( "%{height}" , 290 . ToString ( ) ) ;
73
79
Task < BitmapImage > thumbImage = InfoHelper . GetThumb ( thumbUrl ) ;
@@ -278,14 +284,67 @@ private void BackgroundDownloadManager_DoWork(object sender, DoWorkEventArgs e)
278
284
catch { }
279
285
}
280
286
}
281
-
282
- ( sender as BackgroundWorker ) . ReportProgress ( 0 , "Finalizing MP4 (3/3)" ) ;
287
+
288
+ bool isVFR = false ;
289
+ if ( options . encode_cfr )
290
+ {
291
+ var process = new Process
292
+ {
293
+ StartInfo =
294
+ {
295
+ FileName = "ffmpeg.exe" ,
296
+ Arguments = $ "-i \" " + Path . Combine ( downloadFolder , "output.ts" ) + "\" -vf vfrdet -f null -" ,
297
+ UseShellExecute = false ,
298
+ CreateNoWindow = true ,
299
+ RedirectStandardInput = true ,
300
+ RedirectStandardOutput = true ,
301
+ RedirectStandardError = true
302
+ }
303
+ } ;
304
+ string output = "" ;
305
+ process . ErrorDataReceived += delegate ( object o , DataReceivedEventArgs args )
306
+ {
307
+ if ( args . Data != null && args . Data . Contains ( "Parsed_vfrdet" ) )
308
+ {
309
+ output += args . Data ;
310
+ }
311
+ } ;
312
+ process . Start ( ) ;
313
+ process . BeginErrorReadLine ( ) ;
314
+ process . BeginOutputReadLine ( ) ;
315
+ process . WaitForExit ( ) ;
316
+ double VFR = double . Parse ( output . Substring ( output . IndexOf ( "VFR:" ) + 4 , 8 ) ) ;
317
+ if ( VFR == 0.0 )
318
+ {
319
+ AppendLog ( "Constant framerate detected, no need to re-encode" ) ;
320
+ }
321
+ else
322
+ {
323
+ isVFR = true ;
324
+ AppendLog ( "Detected variable framerate, re-encoding" ) ;
325
+ }
326
+ }
327
+
328
+ if ( isVFR )
329
+ ( sender as BackgroundWorker ) . ReportProgress ( 0 , "Re-encoding MP4 (3/3)" ) ;
330
+ else
331
+ ( sender as BackgroundWorker ) . ReportProgress ( 0 , "Finalizing MP4 (3/3)" ) ;
283
332
string outputConvert = options . filename ;
284
333
Task < IMediaInfo > info = MediaInfo . Get ( Path . Combine ( downloadFolder , "output.ts" ) ) ;
285
334
Task . WaitAll ( info ) ;
286
335
double seekTime = options . crop_begin ;
287
- double seekDuration = info . Result . Duration . TotalSeconds - seekTime - options . crop_end ;
288
- Task < IConversionResult > conversionResult = Conversion . New ( ) . Start ( String . Format ( "-y -i \" {0}\" -ss {1} -analyzeduration {2} -t {3} -avoid_negative_ts make_zero -vcodec copy \" {4}\" " , Path . Combine ( downloadFolder , "output.ts" ) , seekTime . ToString ( ) , int . MaxValue , seekDuration . ToString ( ) , outputConvert ) ) ;
336
+ double seekDuration = Math . Round ( info . Result . Duration . TotalSeconds - seekTime - options . crop_end ) ;
337
+ Task < IConversionResult > conversionResult = null ;
338
+ if ( isVFR )
339
+ {
340
+ int newFps = ( int ) Math . Ceiling ( info . Result . VideoStreams . First ( ) . FrameRate ) ;
341
+ conversionResult = Conversion . New ( ) . Start ( String . Format ( "-y -i \" {0}\" -ss {1} -analyzeduration {2} -t {3} -crf 20 -filter:v fps=fps={4} \" {5}\" " , Path . Combine ( downloadFolder , "output.ts" ) , seekTime . ToString ( ) , int . MaxValue , seekDuration . ToString ( ) , newFps , outputConvert ) ) ;
342
+ }
343
+ else
344
+ {
345
+ conversionResult = Conversion . New ( ) . Start ( String . Format ( "-y -i \" {0}\" -ss {1} -analyzeduration {2} -t {3} -avoid_negative_ts make_zero -acodec copy -vcodec copy \" {4}\" " , Path . Combine ( downloadFolder , "output.ts" ) , seekTime . ToString ( ) , int . MaxValue , seekDuration . ToString ( ) , outputConvert ) ) ;
346
+ }
347
+
289
348
Task . WaitAll ( conversionResult ) ;
290
349
if ( Directory . Exists ( downloadFolder ) )
291
350
DeleteDirectory ( downloadFolder ) ;
@@ -477,6 +536,8 @@ private void Page_Initialized(object sender, EventArgs e)
477
536
SetEnabled ( false ) ;
478
537
WebRequest . DefaultWebProxy = null ;
479
538
numDownloadThreads . Value = Settings . Default . VodDownloadThreads ;
539
+ textOauth . Text = Settings . Default . OAuth ;
540
+ checkCFR . IsChecked = Settings . Default . EncodeCFR ;
480
541
}
481
542
482
543
private void numDownloadThreads_ValueChanged ( object sender , RoutedPropertyChangedEventArgs < object > e )
@@ -487,6 +548,18 @@ private void numDownloadThreads_ValueChanged(object sender, RoutedPropertyChange
487
548
Settings . Default . Save ( ) ;
488
549
}
489
550
}
551
+
552
+ private void textOauth_TextChanged ( object sender , TextChangedEventArgs e )
553
+ {
554
+ Settings . Default . OAuth = textOauth . Text ;
555
+ Settings . Default . Save ( ) ;
556
+ }
557
+
558
+ private void checkCFR_Changed ( object sender , RoutedEventArgs e )
559
+ {
560
+ Settings . Default . EncodeCFR = ( bool ) checkCFR . IsChecked ;
561
+ Settings . Default . Save ( ) ;
562
+ }
490
563
}
491
564
}
492
565
public class ProgressReport
@@ -516,6 +589,7 @@ public class DownloadOptions
516
589
public TimeSpan cropped_end_time { get ; set ; }
517
590
public double crop_end { get ; set ; }
518
591
public int download_threads { get ; set ; }
592
+ public bool encode_cfr { get ; set ; }
519
593
public DownloadOptions ( )
520
594
{
521
595
@@ -535,5 +609,6 @@ public void UpdateValues(PageVodDownload currentPage)
535
609
crop_begin = 0.0 ;
536
610
crop_end = 0.0 ;
537
611
download_threads = ( int ) currentPage . numDownloadThreads . Value ;
612
+ encode_cfr = ( bool ) currentPage . checkCFR . IsChecked ;
538
613
}
539
614
}
0 commit comments