A little Windows Application for creating funscript files and/or converting them to the corresponding csv format.
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

Unpack-Manager.cs 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. using Accord;
  2. using Accord.Video.FFMPEG;
  3. using Ionic.Zip;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.ComponentModel;
  7. using System.Data;
  8. using System.Drawing;
  9. using System.Drawing.Drawing2D;
  10. using System.Drawing.Imaging;
  11. using System.Globalization;
  12. using System.IO;
  13. using System.Linq;
  14. using System.Text;
  15. using System.Threading.Tasks;
  16. using System.Windows.Forms;
  17. namespace Funscripta
  18. {
  19. public partial class Unpack_Manager : Form
  20. {
  21. private static List<PackThread> PackThreads { get; set; } = new List<PackThread>();
  22. private OpenFileDialog ofd = new OpenFileDialog();
  23. private List<string> Videos = new List<string>();
  24. public Unpack_Manager()
  25. {
  26. InitializeComponent();
  27. ofd.Filter = "Media Files|*.mpg;*.avi;*.mov;*.mp4;*.flv|All Files|*.*";
  28. ofd.DefaultExt = ".mp4";
  29. ofd.Multiselect = true;
  30. listView1.DrawColumnHeader += listView1_DrawColumnHeader;
  31. listView1.DrawItem += ListView1_DrawItem;
  32. listView1.DrawSubItem += ListView1_DrawSubItem;
  33. foreach ( var thread in PackThreads )
  34. {
  35. var lvi = new ListViewItem( thread.Video.Name );
  36. thread.Item = lvi;
  37. listView1.Items.Add( lvi );
  38. Videos.Add( thread.Video.FullName );
  39. }
  40. }
  41. private void ListView1_DrawSubItem( object sender, DrawListViewSubItemEventArgs e )
  42. {
  43. TextFormatFlags flags = TextFormatFlags.Left;
  44. using ( StringFormat sf = new StringFormat() )
  45. {
  46. // Store the column text alignment, letting it default
  47. // to Left if it has not been set to Center or Right.
  48. switch ( e.Header.TextAlign )
  49. {
  50. case HorizontalAlignment.Center:
  51. sf.Alignment = StringAlignment.Center;
  52. flags = TextFormatFlags.HorizontalCenter;
  53. break;
  54. case HorizontalAlignment.Right:
  55. sf.Alignment = StringAlignment.Far;
  56. flags = TextFormatFlags.Right;
  57. break;
  58. }
  59. // Draw the text and background for a subitem with a
  60. // negative value.
  61. double subItemValue;
  62. if ( e.ColumnIndex > 0 && Double.TryParse(
  63. e.SubItem.Text, NumberStyles.Currency,
  64. NumberFormatInfo.CurrentInfo, out subItemValue ) &&
  65. subItemValue < 0 )
  66. {
  67. // Unless the item is selected, draw the standard
  68. // background to make it stand out from the gradient.
  69. if ( ( e.ItemState & ListViewItemStates.Selected ) == 0 )
  70. {
  71. e.DrawBackground();
  72. }
  73. // Draw the subitem text in red to highlight it.
  74. e.Graphics.DrawString( e.SubItem.Text,
  75. listView1.Font, Brushes.Red, e.Bounds, sf );
  76. return;
  77. }
  78. // Draw normal text for a subitem with a nonnegative
  79. // or nonnumerical value.
  80. e.DrawText( flags );
  81. }
  82. }
  83. private void ListView1_DrawItem( object sender, DrawListViewItemEventArgs e )
  84. {
  85. if ( ( e.State & ListViewItemStates.Selected ) != 0 )
  86. {
  87. // Draw the background and focus rectangle for a selected item.
  88. e.Graphics.FillRectangle( Brushes.DarkBlue, e.Bounds );
  89. e.DrawFocusRectangle();
  90. }
  91. else
  92. {
  93. // Draw the background for an unselected item.
  94. using ( LinearGradientBrush brush =
  95. new LinearGradientBrush( e.Bounds, Color.DarkBlue,
  96. Color.DarkGreen, LinearGradientMode.Horizontal ) )
  97. {
  98. e.Graphics.FillRectangle( brush, e.Bounds );
  99. }
  100. }
  101. // Draw the item text for views other than the Details view.
  102. if ( listView1.View != View.Details )
  103. {
  104. e.DrawText();
  105. }
  106. }
  107. private void listView1_DrawColumnHeader( object sender, DrawListViewColumnHeaderEventArgs e )
  108. {
  109. var strFormat = new StringFormat();
  110. if ( e.Header.TextAlign == HorizontalAlignment.Center )
  111. strFormat.Alignment = StringAlignment.Center;
  112. else if ( e.Header.TextAlign == HorizontalAlignment.Right )
  113. strFormat.Alignment = StringAlignment.Far;
  114. e.DrawBackground();
  115. e.Graphics.FillRectangle( new SolidBrush( Color.FromArgb( 65, 65, 65 ) ), e.Bounds );
  116. e.Graphics.DrawLine( new Pen( Color.White ), new PointF( e.Bounds.Width + e.Bounds.X - 1, 0 ), new PointF( e.Bounds.Width + e.Bounds.X - 1, e.Bounds.Height ) );
  117. var headerFont = new Font( "Arial", 8, FontStyle.Bold );
  118. e.Graphics.DrawString( e.Header.Text, headerFont, Brushes.White, e.Bounds, strFormat );
  119. }
  120. private void timer1_Tick( object sender, EventArgs e )
  121. {
  122. if ( !backgroundWorker1.IsBusy ) backgroundWorker1.RunWorkerAsync();
  123. }
  124. public class PackThread : IDisposable
  125. {
  126. private bool _frozen = true;
  127. private Task Task { get; set; }
  128. public bool Frozen { get => _frozen; set {
  129. if ( Frozen == value || Done ) return;
  130. if ( value ) Freezing = true;
  131. if ( !value && Task.Status != TaskStatus.Running ) Task.Start();
  132. _frozen = value;
  133. } }
  134. public bool Freezing { get; private set; }
  135. public bool Done { get; private set; } = false;
  136. public FileInfo Video { get; set; }
  137. private DirectoryInfo Raw { get; set; }
  138. private FileInfo RawCompressed { get; set; }
  139. private VideoFileReader VFR { get; set; }
  140. private ZipFile Zip { get; set; }
  141. public ListViewItem Item { get; set; }
  142. public int CurrentFrame { get; private set; }
  143. public long MaxFrames => VFR.FrameCount;
  144. public float Progress => ( ( float ) CurrentFrame ) / MaxFrames * 100;
  145. public float ElementsPerSecond => ElementsPerSeconds.Count > 0 ? ElementsPerSeconds.Average() : 0;
  146. private Buffer<float> ElementsPerSeconds { get; set; } = new Buffer<float>( 10 );
  147. public string TimeLeft => ElementsPerSecond <= 0 ? "infinite" : TimeSpan.FromSeconds( ( MaxFrames - CurrentFrame ) / ElementsPerSecond ).ToString( @"hh\:mm\:ss" );
  148. private static readonly DateTime Jan1st1970 = new DateTime( 1970, 1, 1, 0, 0, 0, DateTimeKind.Utc );
  149. public static long CurrentTimeMillis()
  150. {
  151. return ( long ) ( DateTime.UtcNow - Jan1st1970 ).TotalMilliseconds;
  152. }
  153. public PackThread( FileInfo video )
  154. {
  155. Video = video;
  156. var stripped = Video.FullName.Substring( 0, Video.FullName.Length - Video.Extension.Length );
  157. Raw = new DirectoryInfo( $"{ stripped }.raw" );
  158. RawCompressed = new FileInfo( $"{ stripped }.rawc" );
  159. VFR = new VideoFileReader();
  160. VFR.Open( video.FullName );
  161. Zip = new ZipFile( RawCompressed.FullName );
  162. Task = new Task( () => {
  163. if ( !Raw.Exists ) Raw.Create();
  164. while( true )
  165. {
  166. if ( Frozen )
  167. {
  168. Freezing = false;
  169. return;
  170. }
  171. // Zip.Save( RawCompressed.FullName );
  172. var timestamp = CurrentTimeMillis();
  173. Bitmap bmp = VFR.ReadVideoFrame();
  174. CurrentFrame++;
  175. if ( bmp == null )
  176. {
  177. Zip.Save();
  178. Done = true;
  179. _frozen = true;
  180. return;
  181. }
  182. //var ms = new MemoryStream();
  183. bmp.Save( $"{ Raw.FullName }/{ CurrentFrame }", ImageFormat.Jpeg );
  184. //Zip.AddEntry( CurrentFrame.ToString(), ms );
  185. bmp.Dispose();
  186. var dif = CurrentTimeMillis() - timestamp;
  187. if ( dif != 0 )
  188. {
  189. //ElementsPerSecond = 1000F / dif;
  190. ElementsPerSeconds.Add( 1000F / dif );
  191. }
  192. }
  193. } );
  194. }
  195. public PackThread( string video ) : this( new FileInfo( video ) )
  196. {
  197. }
  198. public void Dispose()
  199. {
  200. // VFR.Dispose();
  201. Zip.Dispose();
  202. }
  203. }
  204. private void backgroundWorker1_DoWork( object sender, DoWorkEventArgs e )
  205. {
  206. if ( PackThreads.Count <= 0 ) return;
  207. var active = 0;
  208. var maxActive = maxActiveThreads.Value;
  209. try
  210. {
  211. foreach ( var thread in PackThreads )
  212. {
  213. listView1.MayInvoke( () => {
  214. for ( var i = thread.Item.SubItems.Count; i < 5; i++ ) thread.Item.SubItems.Add( "" );
  215. thread.Item.SubItems[ 1 ].Text = $"{ thread.Progress.DecimalStrip() } %";
  216. thread.Item.SubItems[ 2 ].Text = $"{ thread.CurrentFrame } / { thread.MaxFrames }";
  217. thread.Item.SubItems[ 3 ].Text = $"{ thread.ElementsPerSecond.DecimalStrip() } items/s";
  218. } );
  219. if ( thread.Done )
  220. {
  221. PackThreads.Remove( thread );
  222. Videos.Remove( thread.Video.FullName );
  223. listView1.MayInvoke( () => thread.Item.SubItems[ 4 ].Text = "Done" );
  224. thread.Dispose();
  225. }
  226. else
  227. {
  228. if ( !thread.Frozen )
  229. {
  230. if ( active < maxActive ) active++;
  231. else thread.Frozen = true;
  232. }
  233. listView1.MayInvoke( () => thread.Item.SubItems[ 4 ].Text = $"{ thread.TimeLeft }" );
  234. }
  235. }
  236. if ( active < maxActive )
  237. {
  238. foreach ( var thread in PackThreads )
  239. {
  240. if ( thread.Frozen && active < maxActive )
  241. {
  242. thread.Frozen = false;
  243. active++;
  244. }
  245. else if ( active >= maxActive ) break;
  246. }
  247. }
  248. } catch ( Exception ex )
  249. {
  250. }
  251. // listView1.MayInvoke( () => listView1.Refresh() );
  252. }
  253. private void button1_Click( object sender, EventArgs e )
  254. {
  255. if ( ofd.ShowDialog() == DialogResult.OK )
  256. {
  257. foreach( var file in ofd.FileNames )
  258. {
  259. var f = new FileInfo( file );
  260. if ( Videos.Contains( f.FullName ) ) continue;
  261. var thread = new PackThread( f );
  262. var lvi = new ListViewItem( f.Name );
  263. thread.Item = lvi;
  264. PackThreads.Add( thread );
  265. listView1.Items.Add( lvi );
  266. Videos.Add( f.FullName );
  267. }
  268. }
  269. }
  270. private void button2_Click( object sender, EventArgs e )
  271. {
  272. foreach( ListViewItem lvi in listView1.SelectedItems )
  273. {
  274. var thread = Find( lvi );
  275. if ( thread == null ) continue;
  276. PackThreads.Remove( thread );
  277. if ( !thread.Frozen ) thread.Frozen = true;
  278. Videos.Remove( thread.Video.FullName );
  279. thread.Dispose();
  280. lvi.SubItems[ 4 ].Text = "Canceled";
  281. }
  282. }
  283. private PackThread Find( ListViewItem lvi )
  284. {
  285. PackThread _thread = null;
  286. foreach( var thread in PackThreads )
  287. {
  288. if ( thread.Item == lvi ) _thread = thread;
  289. }
  290. return _thread;
  291. }
  292. public class Buffer<T> : Queue<T>
  293. {
  294. private int? maxCapacity { get; set; }
  295. public Buffer() { maxCapacity = null; }
  296. public Buffer( int capacity ) { maxCapacity = capacity; }
  297. public void Add( T newElement )
  298. {
  299. if ( this.Count == ( maxCapacity ?? -1 ) ) this.Dequeue(); // no limit if maxCapacity = null
  300. this.Enqueue( newElement );
  301. }
  302. }
  303. }
  304. }