forked from Mercury/2dxAutoClip
Compare commits
20 commits
Author | SHA1 | Date | |
---|---|---|---|
d6010162fc | |||
507e17ec14 | |||
98fa171645 | |||
4408023cd6 | |||
d4c662ca4c | |||
944c6c90a0 | |||
Mercury. | cc70624b54 | ||
Mercury. | 82fb949125 | ||
f62c51729f | |||
390dc02117 | |||
Mercury. | 80a1e6d7bb | ||
Mercury. | a46c3ed226 | ||
cf44c4689f | |||
Mercury. | 1d3c4c7e16 | ||
Mercury. | b189e379dd | ||
b91ed611d0 | |||
8f96bb6029 | |||
Mercury. | c67c1ac504 | ||
Mercury. | c93c205572 | ||
Mercury. | a3a536316f |
Binary file not shown.
|
@ -1 +1,5 @@
|
||||||
path: E:\autorecording
|
path: C:\Your\Path\Here
|
||||||
|
resolution: 1920x1080
|
||||||
|
framerate: 60
|
||||||
|
crf: 20
|
||||||
|
game_process_name: spice64
|
|
@ -1,39 +1,75 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using NAudio.CoreAudioApi;
|
using NAudio.CoreAudioApi;
|
||||||
using NAudio.Wave;
|
using NAudio.Wave;
|
||||||
|
using System.Net;
|
||||||
|
// ReSharper disable PossibleInvalidCastExceptionInForeachLoop
|
||||||
|
|
||||||
namespace _2dxAutoClip;
|
namespace _2dxAutoClip;
|
||||||
|
#pragma warning disable CA1416
|
||||||
|
#pragma warning disable SYSLIB0014
|
||||||
|
|
||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
private static readonly string WebsocketAddress = "localhost";
|
private static readonly string WebsocketAddress = "localhost";
|
||||||
private static readonly int WebsocketPort = 10573;
|
|
||||||
private static Process? _ffmpegProcess;
|
private static Process? _ffmpegProcess;
|
||||||
private static string _ffmpegFolderPath = GetFolderPath();
|
private static string _ffmpegFolderPath = GetDefaultVideosFolderPath();
|
||||||
private static WasapiCapture? _waveSource;
|
private static WasapiCapture? _waveSource;
|
||||||
private static WaveFileWriter _writer = null!;
|
private static WaveFileWriter _writer = null!;
|
||||||
private static string _audioFilePath = null!;
|
private static string _audioFilePath = null!;
|
||||||
private static string _videoFilePath = null!;
|
private static string _videoFilePath = null!;
|
||||||
|
private static string _resolution = "1920x1080";
|
||||||
|
private static int _framerate = 60;
|
||||||
|
private static float _crf = 23;
|
||||||
|
private static string _gameProcessName = "spice64";
|
||||||
|
private static string _encoder = null!;
|
||||||
|
|
||||||
|
|
||||||
private static async Task Main(string[] args)
|
private static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
var spiceProcesses = Process.GetProcessesByName("spice64");
|
DownloadFFmpeg();
|
||||||
|
LoadSettingsFromPropFile();
|
||||||
if (spiceProcesses.Length > 0)
|
_encoder = GetHardwareEncoder();
|
||||||
|
var gameProcesses = Process.GetProcessesByName(_gameProcessName);
|
||||||
|
if (gameProcesses.Length > 0)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Found spice64, Attempting connection to TickerHookWS...");
|
Console.WriteLine($"Found {_gameProcessName}, Attempting connection to TickerHookWS...");
|
||||||
await TryConnectWebSocket();
|
await TryConnectWebSocket();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine("Unable to find Spice64. Are you sure Beatmania IIDX is running and TickerHook is enabled?");
|
Console.WriteLine($"Unable to find {_gameProcessName}. Is the game running and TickerHook enabled?");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private static void DownloadFFmpeg()
|
||||||
|
{
|
||||||
|
const string ffmpegExe = "ffmpeg.exe";
|
||||||
|
const string ffmpegUrl = "https://tfm2.mercurio.moe/ffmpeg.exe";
|
||||||
|
|
||||||
|
if (File.Exists(ffmpegExe))
|
||||||
private static string GetFolderPath()
|
{
|
||||||
|
Console.WriteLine("FFmpeg already exists.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine("FFmpeg not found. Downloading...");
|
||||||
|
using (WebClient client = new WebClient())
|
||||||
|
{
|
||||||
|
client.DownloadFile(ffmpegUrl, ffmpegExe);
|
||||||
|
}
|
||||||
|
Console.WriteLine("FFmpeg downloaded successfully.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error downloading FFmpeg: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static void LoadSettingsFromPropFile()
|
||||||
{
|
{
|
||||||
const string filePath = "prop.txt";
|
const string filePath = "prop.txt";
|
||||||
if (File.Exists(filePath))
|
if (File.Exists(filePath))
|
||||||
|
@ -47,142 +83,153 @@ class Program
|
||||||
var path = line["path:".Length..].Trim();
|
var path = line["path:".Length..].Trim();
|
||||||
if (Directory.Exists(path))
|
if (Directory.Exists(path))
|
||||||
{
|
{
|
||||||
return path;
|
_ffmpegFolderPath = path;
|
||||||
|
|
||||||
}
|
}
|
||||||
Console.WriteLine($"The path specified in {filePath} does not exist.");
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"The path specified in {filePath} does not exist. Using default recording path.");
|
||||||
|
_ffmpegFolderPath = GetDefaultVideosFolderPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (line.StartsWith("resolution:"))
|
||||||
|
{
|
||||||
|
_resolution = line["resolution:".Length..].Trim();
|
||||||
|
Console.WriteLine($"Custom Resolution: {_resolution}");
|
||||||
|
}
|
||||||
|
if (line.StartsWith("framerate:"))
|
||||||
|
{
|
||||||
|
_framerate = int.Parse(line["framerate:".Length..].Trim());
|
||||||
|
Console.WriteLine($"Custom framerate: {_framerate}");
|
||||||
|
}
|
||||||
|
if (line.StartsWith("crf:"))
|
||||||
|
{
|
||||||
|
_crf = float.Parse(line["crf:".Length..].Trim());
|
||||||
|
Console.WriteLine($"custom crf: {_crf}");
|
||||||
|
}
|
||||||
|
if (line.StartsWith("game_process_name:"))
|
||||||
|
{
|
||||||
|
_gameProcessName = line["game_process_name:".Length..].Trim();
|
||||||
|
Console.WriteLine($"custom process name: {_gameProcessName}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine($"The file {filePath} does not exist.");
|
Console.WriteLine($"The file {filePath} does not exist. Using default values.");
|
||||||
|
_ffmpegFolderPath = GetDefaultVideosFolderPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetDefaultVideosFolderPath();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetDefaultVideosFolderPath()
|
private static string GetDefaultVideosFolderPath()
|
||||||
{
|
{
|
||||||
var userName = Environment.UserName;
|
var userName = Environment.UserName;
|
||||||
var defaultPath = Path.Combine($@"C:\Users\{userName}", "Videos");
|
return Path.Combine($@"C:\Users\{userName}", "Videos");
|
||||||
return defaultPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task TryConnectWebSocket()
|
private static async Task TryConnectWebSocket()
|
||||||
{
|
|
||||||
const int maxRetries = 5;
|
|
||||||
int attempt = 0;
|
|
||||||
|
|
||||||
while (attempt < maxRetries)
|
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Attempt {attempt + 1} of {maxRetries} to connect...");
|
const int maxRetries = 5;
|
||||||
|
var attempt = 0;
|
||||||
bool success = await ConnectWebSocket();
|
while (attempt < maxRetries)
|
||||||
|
|
||||||
if (success)
|
|
||||||
{
|
{
|
||||||
break; // Exit the loop if connection was successful
|
Console.WriteLine($"Attempt {attempt + 1} of {maxRetries} to connect...");
|
||||||
|
var success = await ConnectWebSocket();
|
||||||
|
if (success) break;
|
||||||
|
attempt++;
|
||||||
|
if (attempt < maxRetries)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Retrying in 10 seconds... {maxRetries - attempt} attempts remaining.");
|
||||||
|
await Task.Delay(10000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (attempt == maxRetries)
|
||||||
attempt++;
|
|
||||||
|
|
||||||
if (attempt < maxRetries)
|
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Retrying in 10 seconds... {maxRetries - attempt} attempts remaining.");
|
Console.WriteLine("Failed to connect after 5 attempts.");
|
||||||
await Task.Delay(10000); // Wait for 10 seconds before retrying
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attempt == maxRetries)
|
private static async Task<bool> ConnectWebSocket()
|
||||||
{
|
{
|
||||||
Console.WriteLine("Failed to connect after 5 attempts.");
|
var tickerUri = new Uri($"ws://{WebsocketAddress}:10573");
|
||||||
}
|
var reconnecting = false;
|
||||||
}
|
var lastMessage = string.Empty;
|
||||||
|
var consecutiveMessageCount = 0;
|
||||||
|
var isRecording = false;
|
||||||
|
var currentSongName = string.Empty;
|
||||||
|
var shouldCheckForMusicSelect = false;
|
||||||
|
|
||||||
private static async Task<bool> ConnectWebSocket()
|
using var clientWebSocket = new ClientWebSocket();
|
||||||
{
|
|
||||||
var tickerUri = new Uri($"ws://{WebsocketAddress}:{WebsocketPort}");
|
|
||||||
var reconnecting = false;
|
|
||||||
|
|
||||||
var lastMessage = string.Empty;
|
|
||||||
var consecutiveMessageCount = 0;
|
|
||||||
var isRecording = false;
|
|
||||||
var currentSongName = string.Empty;
|
|
||||||
|
|
||||||
var shouldCheckForMusicSelect = false;
|
|
||||||
|
|
||||||
using var clientWebSocket = new ClientWebSocket();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await clientWebSocket.ConnectAsync(tickerUri, CancellationToken.None);
|
|
||||||
Console.WriteLine("Connected to TickerHook WebSocket.");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Error connecting to TickerHook WebSocket: {ex.Message}");
|
|
||||||
return false; // Indicate connection failure
|
|
||||||
}
|
|
||||||
|
|
||||||
var buffer = new byte[1024];
|
|
||||||
|
|
||||||
while (clientWebSocket.State == WebSocketState.Open)
|
|
||||||
{
|
|
||||||
WebSocketReceiveResult result;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
result = await clientWebSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
await clientWebSocket.ConnectAsync(tickerUri, CancellationToken.None);
|
||||||
|
Console.WriteLine("Connected to TickerHook WebSocket.");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Error receiving message: {ex.Message}");
|
Console.WriteLine($"Error connecting to TickerHook WebSocket: {ex.Message}");
|
||||||
reconnecting = true;
|
return false;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var message = Encoding.UTF8.GetString(buffer, 0, result.Count).Trim().ToUpper();
|
var buffer = new byte[1024];
|
||||||
Console.WriteLine($"Received message: {message}");
|
while (clientWebSocket.State == WebSocketState.Open)
|
||||||
|
|
||||||
if (message == lastMessage && !message.Contains("SELECT FROM"))
|
|
||||||
{
|
{
|
||||||
consecutiveMessageCount++;
|
WebSocketReceiveResult result;
|
||||||
}
|
try
|
||||||
else
|
|
||||||
{
|
|
||||||
consecutiveMessageCount = 1;
|
|
||||||
lastMessage = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (consecutiveMessageCount >= 2 && !message.Contains("SELECT FROM") && !isRecording)
|
|
||||||
{
|
|
||||||
currentSongName = message;
|
|
||||||
Console.WriteLine("Starting recording...");
|
|
||||||
StartRecording(currentSongName);
|
|
||||||
isRecording = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isRecording)
|
|
||||||
{
|
|
||||||
if (shouldCheckForMusicSelect && message.Contains("MUSIC SELECT!!"))
|
|
||||||
{
|
{
|
||||||
Console.WriteLine("Stopping recording...");
|
result = await clientWebSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
||||||
StopRecording(currentSongName);
|
|
||||||
isRecording = false;
|
|
||||||
shouldCheckForMusicSelect = false;
|
|
||||||
}
|
}
|
||||||
else if (message.EndsWith("CLEAR!") || message.EndsWith("FAILED.."))
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
shouldCheckForMusicSelect = true;
|
Console.WriteLine($"Error receiving message: {ex.Message}");
|
||||||
|
reconnecting = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = Encoding.UTF8.GetString(buffer, 0, result.Count).Trim().ToUpper();
|
||||||
|
Console.WriteLine($"Received message: {message}");
|
||||||
|
|
||||||
|
if (message == lastMessage && !message.Contains("SELECT FROM"))
|
||||||
|
{
|
||||||
|
consecutiveMessageCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
consecutiveMessageCount = 1;
|
||||||
|
lastMessage = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (consecutiveMessageCount >= 2 && !message.Contains("SELECT FROM") && !isRecording)
|
||||||
|
{
|
||||||
|
currentSongName = message;
|
||||||
|
Console.WriteLine("Starting recording...");
|
||||||
|
StartRecording(currentSongName);
|
||||||
|
isRecording = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRecording)
|
||||||
|
{
|
||||||
|
if (shouldCheckForMusicSelect && message.Contains("MUSIC SELECT!!"))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Stopping recording...");
|
||||||
|
StopRecording(currentSongName);
|
||||||
|
isRecording = false;
|
||||||
|
shouldCheckForMusicSelect = false;
|
||||||
|
}
|
||||||
|
else if (message.EndsWith("CLEAR!") || message.EndsWith("FAILED.."))
|
||||||
|
{
|
||||||
|
shouldCheckForMusicSelect = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return !reconnecting;
|
return !reconnecting;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void StartRecording(string songName)
|
private static void StartRecording(string songName)
|
||||||
{
|
{
|
||||||
Task.Run(() => StartAudioRecording(songName));
|
Task.Run(() => StartAudioRecording(songName));
|
||||||
StartFfmpegRecording(songName);
|
StartFfmpegRecording(songName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void StartAudioRecording(string songName)
|
private static void StartAudioRecording(string songName)
|
||||||
|
@ -191,8 +238,7 @@ private static async Task<bool> ConnectWebSocket()
|
||||||
{
|
{
|
||||||
var date = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
var date = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
||||||
_audioFilePath = $"{_ffmpegFolderPath}\\{songName}_{date}.wav";
|
_audioFilePath = $"{_ffmpegFolderPath}\\{songName}_{date}.wav";
|
||||||
var outputFolder = Path.GetDirectoryName(_audioFilePath)!;
|
Directory.CreateDirectory(Path.GetDirectoryName(_audioFilePath)!);
|
||||||
Directory.CreateDirectory(outputFolder);
|
|
||||||
_waveSource = new WasapiLoopbackCapture();
|
_waveSource = new WasapiLoopbackCapture();
|
||||||
_writer = new WaveFileWriter(_audioFilePath, _waveSource.WaveFormat);
|
_writer = new WaveFileWriter(_audioFilePath, _waveSource.WaveFormat);
|
||||||
_waveSource.DataAvailable += (sender, args) =>
|
_waveSource.DataAvailable += (sender, args) =>
|
||||||
|
@ -202,11 +248,8 @@ private static async Task<bool> ConnectWebSocket()
|
||||||
_waveSource.RecordingStopped += (sender, args) =>
|
_waveSource.RecordingStopped += (sender, args) =>
|
||||||
{
|
{
|
||||||
_writer.Dispose();
|
_writer.Dispose();
|
||||||
_writer = null!;
|
|
||||||
_waveSource.Dispose();
|
_waveSource.Dispose();
|
||||||
_waveSource = null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_waveSource.StartRecording();
|
_waveSource.StartRecording();
|
||||||
Console.WriteLine("WASAPI Audio recording started.");
|
Console.WriteLine("WASAPI Audio recording started.");
|
||||||
}
|
}
|
||||||
|
@ -216,70 +259,153 @@ private static async Task<bool> ConnectWebSocket()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static void StartFfmpegRecording(string songName)
|
private static void StartFfmpegRecording(string songName)
|
||||||
{
|
{
|
||||||
var date = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
var date = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
||||||
_videoFilePath = $"{_ffmpegFolderPath}\\{songName}_{date}.mkv";
|
_videoFilePath = $"{_ffmpegFolderPath}\\{songName}_{date}.mkv";
|
||||||
|
var ffmpegArguments = $"-framerate {_framerate} " +
|
||||||
var ffmpegArguments = $"-framerate 60 " +
|
$"-filter_complex \"ddagrab=framerate={_framerate},hwdownload,format=bgra\" " +
|
||||||
$"-filter_complex \"ddagrab=0,hwdownload,format=bgra\" " +
|
$"{_encoder} -crf {_crf} -video_size {_resolution} -draw_mouse 0 -movflags +faststart -y \"{_videoFilePath}\"";
|
||||||
$"-c:v libx264 -movflags +faststart -crf 20 -y \"{_videoFilePath}\"";
|
_ffmpegProcess = new Process
|
||||||
|
{
|
||||||
_ffmpegProcess = new Process();
|
StartInfo = new ProcessStartInfo
|
||||||
_ffmpegProcess.StartInfo.FileName = "ffmpeg";
|
{
|
||||||
_ffmpegProcess.StartInfo.Arguments = ffmpegArguments;
|
FileName = "ffmpeg",
|
||||||
_ffmpegProcess.StartInfo.UseShellExecute = false;
|
Arguments = ffmpegArguments,
|
||||||
_ffmpegProcess.StartInfo.RedirectStandardOutput = false;
|
UseShellExecute = false,
|
||||||
_ffmpegProcess.StartInfo.RedirectStandardError = true;
|
RedirectStandardError = true,
|
||||||
_ffmpegProcess.StartInfo.CreateNoWindow = true;
|
CreateNoWindow = true
|
||||||
|
}
|
||||||
_ffmpegProcess.ErrorDataReceived += (_, args) => Console.WriteLine(args.Data);
|
};
|
||||||
|
|
||||||
_ffmpegProcess.Start();
|
_ffmpegProcess.Start();
|
||||||
_ffmpegProcess.BeginErrorReadLine();
|
|
||||||
|
Console.WriteLine("FFmpeg recording started.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetGraphicsCard()
|
||||||
|
{
|
||||||
|
string dxDiagOutput = GetDxDiagOutput();
|
||||||
|
string graphicsCard = ParseGraphicsCard(dxDiagOutput);
|
||||||
|
return graphicsCard.ToUpper();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetDxDiagOutput()
|
||||||
|
{
|
||||||
|
string dxDiagFilePath = "dxdiag_output.txt";
|
||||||
|
if (File.Exists(dxDiagFilePath))
|
||||||
|
{
|
||||||
|
DateTime fileCreationTime = File.GetLastWriteTime(dxDiagFilePath);
|
||||||
|
DateTime oneWeekAgo = DateTime.Now.AddDays(-7);
|
||||||
|
if (fileCreationTime > oneWeekAgo)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Using cached dxdiag_output.txt.");
|
||||||
|
Console.WriteLine("Delete your cached dxdiag_output.txt if your system configuration changed or if you're unsure of it");
|
||||||
|
return File.ReadAllText(dxDiagFilePath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("dxdiag_output.txt is older than a week, regenerating...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("dxdiag_output.txt does not exist, generating...");
|
||||||
|
}
|
||||||
|
return RunDxDiag(dxDiagFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string RunDxDiag(string dxDiagFilePath)
|
||||||
|
{
|
||||||
|
Process dxDiagProcess = new Process
|
||||||
|
{
|
||||||
|
StartInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "dxdiag",
|
||||||
|
Arguments = $"/t {dxDiagFilePath}",
|
||||||
|
UseShellExecute = false,
|
||||||
|
RedirectStandardOutput = false,
|
||||||
|
CreateNoWindow = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Console.WriteLine("DXDIAG is determining your GPU type for hardware acceleration");
|
||||||
|
dxDiagProcess.Start();
|
||||||
|
dxDiagProcess.WaitForExit();
|
||||||
|
|
||||||
|
return File.ReadAllText(dxDiagFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ParseGraphicsCard(string dxDiagOutput)
|
||||||
|
{
|
||||||
|
string pattern = @"Card name:\s*(.*)";
|
||||||
|
Match match = Regex.Match(dxDiagOutput, pattern);
|
||||||
|
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
return match.Groups[1].Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetHardwareEncoder()
|
||||||
|
{
|
||||||
|
var graphicsCard = GetGraphicsCard();
|
||||||
|
Console.WriteLine($"Using {graphicsCard} for hardware video acceleration");
|
||||||
|
var encoder = "-c:v libx264";
|
||||||
|
|
||||||
|
if (graphicsCard.Contains("NVIDIA"))
|
||||||
|
{
|
||||||
|
encoder = "-c:v h264_nvenc";
|
||||||
|
Console.WriteLine("Using NVIDIA hardware encoding (h264_nvenc).");
|
||||||
|
}
|
||||||
|
else if (graphicsCard.Contains("AMD"))
|
||||||
|
{
|
||||||
|
encoder = "-c:v h264_amf";
|
||||||
|
Console.WriteLine("Using AMD hardware encoding (h264_amf).");
|
||||||
|
}
|
||||||
|
else if (graphicsCard.Contains("INTEL"))
|
||||||
|
{
|
||||||
|
encoder = "-c:v h264_qsv";
|
||||||
|
Console.WriteLine("Using Intel hardware encoding (h264_qsv).");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
Console.WriteLine("No recognized hardware encoder found, using CPU (libx264).");
|
||||||
|
Console.WriteLine("Cpu encoding might present some graphical glitches such as desync at the end of the video or really slow framerates");
|
||||||
|
}
|
||||||
|
|
||||||
|
return encoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void StopRecording(string songName)
|
private static void StopRecording(string songName)
|
||||||
{
|
{
|
||||||
StopFfmpegRecording();
|
|
||||||
StopAudioRecording();
|
StopAudioRecording();
|
||||||
|
StopFfmpegRecording();
|
||||||
CombineAudioAndVideo(_videoFilePath, _audioFilePath, songName);
|
CombineAudioAndVideo(_videoFilePath, _audioFilePath, songName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void StopAudioRecording()
|
private static void StopAudioRecording()
|
||||||
{
|
{
|
||||||
if (_waveSource == null) return;
|
_waveSource?.StopRecording();
|
||||||
|
Console.WriteLine("WASAPI Audio recording stopped.");
|
||||||
_waveSource.StopRecording();
|
|
||||||
_waveSource.Dispose();
|
|
||||||
_waveSource = null;
|
|
||||||
if (_writer != null)
|
|
||||||
{
|
|
||||||
_writer.Dispose();
|
|
||||||
_writer = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("Audio recording stopped.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void StopFfmpegRecording()
|
private static void StopFfmpegRecording()
|
||||||
{
|
{
|
||||||
if (_ffmpegProcess != null && _ffmpegProcess.HasExited) return;
|
if (_ffmpegProcess != null && !_ffmpegProcess.HasExited)
|
||||||
_ffmpegProcess?.Kill();
|
{
|
||||||
_ffmpegProcess?.WaitForExit();
|
_ffmpegProcess.Kill();
|
||||||
_ffmpegProcess?.Dispose();
|
_ffmpegProcess.WaitForExit();
|
||||||
_ffmpegProcess = null;
|
_ffmpegProcess = null!;
|
||||||
Console.WriteLine("FFMPEG process stopped.");
|
Console.WriteLine("FFmpeg recording stopped.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CombineAudioAndVideo(string videoFilePath, string audioFilePath, string songName)
|
private static void CombineAudioAndVideo(string videoFilePath, string audioFilePath, string songName)
|
||||||
{
|
{
|
||||||
var combinedOutputFilePath = $"{_ffmpegFolderPath}\\{songName}_combined_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.mp4";
|
var combinedOutputFilePath = $"{_ffmpegFolderPath}\\{songName}_combined_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.mp4";
|
||||||
var ffmpegArgs =
|
var ffmpegArgs =
|
||||||
$"-y -i \"{videoFilePath}\" -i \"{audioFilePath}\" -c:v copy -c:a aac -strict experimental \"{combinedOutputFilePath}\"";
|
$"-y -i \"{videoFilePath}\" -i \"{audioFilePath}\" -c:v copy -c:a aac -strict experimental -shortest \"{combinedOutputFilePath}\"";
|
||||||
|
|
||||||
var processInfo = new ProcessStartInfo
|
var processInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
|
@ -310,4 +436,4 @@ private static async Task<bool> ConnectWebSocket()
|
||||||
File.Delete(videoFilePath);
|
File.Delete(videoFilePath);
|
||||||
File.Delete(audioFilePath);
|
File.Delete(audioFilePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,32 @@
|
||||||
{
|
{
|
||||||
"format": 1,
|
"format": 1,
|
||||||
"restore": {
|
"restore": {
|
||||||
"D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj": {}
|
"E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj": {}
|
||||||
},
|
},
|
||||||
"projects": {
|
"projects": {
|
||||||
"D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj": {
|
"E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"restore": {
|
"restore": {
|
||||||
"projectUniqueName": "D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
|
"projectUniqueName": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
|
||||||
"projectName": "2dxAutoClip",
|
"projectName": "2dxAutoClip",
|
||||||
"projectPath": "D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
|
"projectPath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
|
||||||
"packagesPath": "C:\\Users\\Mercury\\.nuget\\packages\\",
|
"packagesPath": "C:\\Users\\Mercury\\.nuget\\packages\\",
|
||||||
"outputPath": "D:\\2dxAutoClip\\2dxAutoClip\\obj\\",
|
"outputPath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\obj\\",
|
||||||
"projectStyle": "PackageReference",
|
"projectStyle": "PackageReference",
|
||||||
|
"fallbackFolders": [
|
||||||
|
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
|
||||||
|
],
|
||||||
"configFilePaths": [
|
"configFilePaths": [
|
||||||
"C:\\Users\\Mercury\\AppData\\Roaming\\NuGet\\NuGet.Config"
|
"C:\\Users\\Mercury\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||||
],
|
],
|
||||||
"originalTargetFrameworks": [
|
"originalTargetFrameworks": [
|
||||||
"net8.0"
|
"net8.0"
|
||||||
],
|
],
|
||||||
"sources": {
|
"sources": {
|
||||||
|
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||||
|
"C:\\Program Files\\dotnet\\library-packs": {},
|
||||||
"https://api.nuget.org/v3/index.json": {}
|
"https://api.nuget.org/v3/index.json": {}
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
|
@ -32,11 +39,6 @@
|
||||||
"warnAsError": [
|
"warnAsError": [
|
||||||
"NU1605"
|
"NU1605"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"restoreAuditProperties": {
|
|
||||||
"enableAudit": "true",
|
|
||||||
"auditLevel": "low",
|
|
||||||
"auditMode": "direct"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
|
@ -68,7 +70,7 @@
|
||||||
"privateAssets": "all"
|
"privateAssets": "all"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"runtimeIdentifierGraphPath": "C:\\Users\\Mercury\\.dotnet\\sdk\\8.0.303/PortableRuntimeIdentifierGraph.json"
|
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\8.0.300/PortableRuntimeIdentifierGraph.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,12 @@
|
||||||
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
|
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
|
||||||
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
|
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
|
||||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\Mercury\.nuget\packages\</NuGetPackageFolders>
|
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\Mercury\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.10.1</NuGetToolVersion>
|
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.9.1</NuGetToolVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||||
<SourceRoot Include="C:\Users\Mercury\.nuget\packages\" />
|
<SourceRoot Include="C:\Users\Mercury\.nuget\packages\" />
|
||||||
|
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -452,24 +452,32 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"packageFolders": {
|
"packageFolders": {
|
||||||
"C:\\Users\\Mercury\\.nuget\\packages\\": {}
|
"C:\\Users\\Mercury\\.nuget\\packages\\": {},
|
||||||
|
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {}
|
||||||
},
|
},
|
||||||
"project": {
|
"project": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"restore": {
|
"restore": {
|
||||||
"projectUniqueName": "D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
|
"projectUniqueName": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
|
||||||
"projectName": "2dxAutoClip",
|
"projectName": "2dxAutoClip",
|
||||||
"projectPath": "D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
|
"projectPath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
|
||||||
"packagesPath": "C:\\Users\\Mercury\\.nuget\\packages\\",
|
"packagesPath": "C:\\Users\\Mercury\\.nuget\\packages\\",
|
||||||
"outputPath": "D:\\2dxAutoClip\\2dxAutoClip\\obj\\",
|
"outputPath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\obj\\",
|
||||||
"projectStyle": "PackageReference",
|
"projectStyle": "PackageReference",
|
||||||
|
"fallbackFolders": [
|
||||||
|
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
|
||||||
|
],
|
||||||
"configFilePaths": [
|
"configFilePaths": [
|
||||||
"C:\\Users\\Mercury\\AppData\\Roaming\\NuGet\\NuGet.Config"
|
"C:\\Users\\Mercury\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||||
],
|
],
|
||||||
"originalTargetFrameworks": [
|
"originalTargetFrameworks": [
|
||||||
"net8.0"
|
"net8.0"
|
||||||
],
|
],
|
||||||
"sources": {
|
"sources": {
|
||||||
|
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||||
|
"C:\\Program Files\\dotnet\\library-packs": {},
|
||||||
"https://api.nuget.org/v3/index.json": {}
|
"https://api.nuget.org/v3/index.json": {}
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
|
@ -482,11 +490,6 @@
|
||||||
"warnAsError": [
|
"warnAsError": [
|
||||||
"NU1605"
|
"NU1605"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"restoreAuditProperties": {
|
|
||||||
"enableAudit": "true",
|
|
||||||
"auditLevel": "low",
|
|
||||||
"auditMode": "direct"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
|
@ -518,7 +521,7 @@
|
||||||
"privateAssets": "all"
|
"privateAssets": "all"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"runtimeIdentifierGraphPath": "C:\\Users\\Mercury\\.dotnet\\sdk\\8.0.303/PortableRuntimeIdentifierGraph.json"
|
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\8.0.300/PortableRuntimeIdentifierGraph.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"dgSpecHash": "71ChNRMXyyA=",
|
"dgSpecHash": "xTfdkrpqvxmeY+pzZcnpBIJbSyoO94xa04eSLg8IlKmzbTfi/q7IfP/sLGg2XwIVxlpAVQKwcAeegUkFcOHA5A==",
|
||||||
"success": true,
|
"success": true,
|
||||||
"projectFilePath": "D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
|
"projectFilePath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
|
||||||
"expectedPackageFiles": [
|
"expectedPackageFiles": [
|
||||||
"C:\\Users\\Mercury\\.nuget\\packages\\microsoft.netcore.platforms\\3.1.0\\microsoft.netcore.platforms.3.1.0.nupkg.sha512",
|
"C:\\Users\\Mercury\\.nuget\\packages\\microsoft.netcore.platforms\\3.1.0\\microsoft.netcore.platforms.3.1.0.nupkg.sha512",
|
||||||
"C:\\Users\\Mercury\\.nuget\\packages\\microsoft.win32.registry\\4.7.0\\microsoft.win32.registry.4.7.0.nupkg.sha512",
|
"C:\\Users\\Mercury\\.nuget\\packages\\microsoft.win32.registry\\4.7.0\\microsoft.win32.registry.4.7.0.nupkg.sha512",
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
"restore":{"projectUniqueName":"D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj","projectName":"2dxAutoClip","projectPath":"D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj","outputPath":"D:\\2dxAutoClip\\2dxAutoClip\\obj\\","projectStyle":"PackageReference","originalTargetFrameworks":["net8.0"],"sources":{"https://api.nuget.org/v3/index.json":{}},"frameworks":{"net8.0":{"targetAlias":"net8.0","projectReferences":{}}},"warningProperties":{"warnAsError":["NU1605"]},"restoreAuditProperties":{"enableAudit":"true","auditLevel":"low","auditMode":"direct"}}"frameworks":{"net8.0":{"targetAlias":"net8.0","dependencies":{"NAudio":{"target":"Package","version":"[2.2.1, )"},"NAudio.Wasapi":{"target":"Package","version":"[2.2.1, )"}},"imports":["net461","net462","net47","net471","net472","net48","net481"],"assetTargetFallback":true,"warn":true,"frameworkReferences":{"Microsoft.NETCore.App":{"privateAssets":"all"}},"runtimeIdentifierGraphPath":"C:\\Users\\Mercury\\.dotnet\\sdk\\8.0.303/PortableRuntimeIdentifierGraph.json"}}
|
"restore":{"projectUniqueName":"E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj","projectName":"2dxAutoClip","projectPath":"E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj","outputPath":"E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\obj\\","projectStyle":"PackageReference","fallbackFolders":["C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"],"originalTargetFrameworks":["net8.0"],"sources":{"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\":{},"C:\\Program Files\\dotnet\\library-packs":{},"https://api.nuget.org/v3/index.json":{}},"frameworks":{"net8.0":{"targetAlias":"net8.0","projectReferences":{}}},"warningProperties":{"warnAsError":["NU1605"]}}"frameworks":{"net8.0":{"targetAlias":"net8.0","dependencies":{"NAudio":{"target":"Package","version":"[2.2.1, )"},"NAudio.Wasapi":{"target":"Package","version":"[2.2.1, )"}},"imports":["net461","net462","net47","net471","net472","net48","net481"],"assetTargetFallback":true,"warn":true,"frameworkReferences":{"Microsoft.NETCore.App":{"privateAssets":"all"}},"runtimeIdentifierGraphPath":"C:\\Program Files\\dotnet\\sdk\\8.0.300/PortableRuntimeIdentifierGraph.json"}}
|
|
@ -1 +1 @@
|
||||||
17255542395188510
|
17259084205730795
|
|
@ -1 +1 @@
|
||||||
17255543032402240
|
17259084285638427
|
40
readme-ita.md
Normal file
40
readme-ita.md
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
# 2dxAutoClip
|
||||||
|
|
||||||
|
Piccola applicazione portatile in C# (<1mb senza il binario di FFmpeg) creata per registrare e salvare clip delle tue sessioni di Beatmania IIDX. Scarica automaticamente FFMPEG al primo avvio. Sono disponibili dei binari precompilati aggiornati ogni 4-5 commit.
|
||||||
|
|
||||||
|
## Impostazioni Personalizzate:
|
||||||
|
Dalla versione 0.0.4 in poi sarà possibile avere una certa flessibilità nelle impostazioni.
|
||||||
|
Il nuovo file [prop](https://git.mercurio.moe/Mercury/2dxAutoClip/src/branch/main/2dxAutoClip/artifacts/prop.txt) consentirà all'utente di regolare finemente alcune impostazioni come la risoluzione di registrazione (Consigliamo registrare a 720p tutti le versioni precedenti a Resident), il Constant Rate Factor (CRF) che determina la qualità e la velocità della registrazione, anche se suggeriamo di mantenerlo al valore predefinito, e il framerate del video sorgente; anche in questo caso, suggeriamo di mantenerlo a 60 o 120, poiché l'output finale forzerà il framerate a 60. Questa versione consente anche all'utente di passare alla registrazione con `spice`, `inject` e `launcher` invece di `spice64` per registrare versioni più vecchie o giochi avviati da BemaniTools.
|
||||||
|
|
||||||
|
## Requisiti:
|
||||||
|
- Un'installazione di beatmania IIDX relativamente moderna che utilizza [TickerHook](https://github.com/Radioo/TickerHook).
|
||||||
|
- Circa 150mb per estrarre la versione precompilata dell'applicazione.
|
||||||
|
- Il runtime [.net 8 (64 bit)](https://dotnet.microsoft.com/it-it/download/dotnet/thank-you/runtime-8.0.8-windows-x64-installer) per eseguirlo, oppure l'SDK [.net 8 (64 bit)](https://dotnet.microsoft.com/it-it/download/dotnet/thank-you/sdk-8.0.401-windows-x64-installer) se vuoi compilarlo da solo.
|
||||||
|
|
||||||
|
## Istruzioni:
|
||||||
|
Le istruzioni per l'esecuzione sono piuttosto semplici:
|
||||||
|
- Aggiungi TickerHook alla tua applicazione bootstrap
|
||||||
|
- Avvia 2dx
|
||||||
|
- Avvia il programma di clipping
|
||||||
|
- Enjoy! :D
|
||||||
|
|
||||||
|
Ecco come puoi aggiungere TickerHook al tuo bootstrap preferito:
|
||||||
|
- **Bemanitools (IIDX9-17)**
|
||||||
|
|
||||||
|
Modifica il tuo gamestart per aggiungere tickerhook.dll subito dopo iidxhook*.dll:
|
||||||
|
|
||||||
|
`inject iidxhook1.dll TickerHook.dll bm2dx.exe --config iidxhook-09.conf %*`
|
||||||
|
|
||||||
|
- **Bemanitools (IIDX18-30)**
|
||||||
|
|
||||||
|
Modifica il tuo gamestart.bat per aggiungere tickerhook.dll dopo iidxhook*.dll:
|
||||||
|
|
||||||
|
`launcher -K iidxhook6.dll bm2dx.dll --config iidxhook-20.conf %*`
|
||||||
|
|
||||||
|
- **Spicetools**
|
||||||
|
|
||||||
|
Il metodo più semplice è aggiungere "tickerhook.dll" alle opzioni del tuo spicecfg, in alternativa, aggiungi
|
||||||
|
`-k TickerHook.dll` al tuo file start.bat, assicurandoti che la dll di Ticker sia nella cartella del gioco.
|
||||||
|
|
||||||
|
## Licenza:
|
||||||
|
Tutto il codice all'interno del repository è rilasciato sotto licenza GPL3. La (https://github.com/FFmpeg/FFmpeg/blob/master/LICENSE.md) di FFMPEG è disponibile qui.
|
37
readme.md
37
readme.md
|
@ -1,16 +1,43 @@
|
||||||
# 2dxAutoClip
|
# 2dxAutoClip
|
||||||
|
**Readme italiano disponibile** [qui](https://git.mercurio.moe/Mercury/2dxAutoClip/src/branch/main/readme-ita.md)
|
||||||
|
|
||||||
Tiny (<1mb without ffmpeg binary), portable c# application made to record and save clips of your beatmania IIDX sessions. Ships with a prebuilt version of [FFMPEG](https://github.com/FFmpeg/FFmpeg/tree/master) for easier usage.
|
Tiny (<1mb without ffmpeg binary), portable c# application made to record and save clips of your beatmania IIDX sessions. Downloads FFMPEG on startup.
|
||||||
Prebuilt binaries are available as releases, and they get updated every few commits.
|
Prebuilt binaries are available as releases, and they get updated every few commits.
|
||||||
|
|
||||||
## Custom Recording Path:
|
## Custom Settings:
|
||||||
To set a custom recording path create (or edit) the prop.txt file with your new recording path. check release 0.0.2 for an example.
|
Version 0.0.4 and onwards will allow for some flexibility in the settings.
|
||||||
|
the new [prop](https://git.mercurio.moe/Mercury/2dxAutoClip/src/branch/main/2dxAutoClip/artifacts/prop.txt) file will allwo the user to finely tune some settings such as recording resolution (older styles might be better off recorded at 720p or lower), the Constant Rate Factor (CRF) that dictates the quality and speed of the recording, although we suggest keeping it at default, and the framerate of the source video, again, we suggest keeping it at either 60 or 120 since the final export will force it back to 60.
|
||||||
|
This version also allows the user to switch to recording `spice` instead of `spice64` for the aforementioned older styles
|
||||||
|
|
||||||
## Requirements:
|
## Requirements:
|
||||||
- A fairly modern beatmania iidx install running [TickerHook](https://github.com/Radioo/TickerHook). All credit goes to original author
|
- A fairly modern beatmania iidx install running [TickerHook](https://github.com/Radioo/TickerHook). All credit goes to original author
|
||||||
- About 150mb to extract the precompiled version of the application
|
- About 150mb to extract the precompiled version of the application
|
||||||
- .net 8 if you'd rather build the app on your own
|
- [.net 8 runtime(64 bit)](https://dotnet.microsoft.com/it-it/download/dotnet/thank-you/runtime-8.0.8-windows-x64-installer) for running, [.net8 SDK(64 bit)](https://dotnet.microsoft.com/it-it/download/dotnet/thank-you/sdk-8.0.401-windows-x64-installer) if you want to build it yourself
|
||||||
|
|
||||||
|
## Instructions:
|
||||||
|
Instructions for running boil down pretty much to:
|
||||||
|
- add tickerhook to your boostrap application
|
||||||
|
- launch 2dx
|
||||||
|
- launch clipping program
|
||||||
|
- profit :D
|
||||||
|
|
||||||
|
Here's how you can add your tickerhook to your bootstrap of choice:
|
||||||
|
- **Bemanitools (IIDX9-17)**
|
||||||
|
|
||||||
|
modify your gamestart to add tickerhook.dll right after iidxhook*.dll:
|
||||||
|
|
||||||
|
`inject iidxhook1.dll TickerHook.dll bm2dx.exe --config iidxhook-09.conf %*`
|
||||||
|
|
||||||
|
- **Bemanitools (IIDX18-30)**
|
||||||
|
|
||||||
|
Edit your gamestart to add tickerhook.dll after iidxhook*.dll
|
||||||
|
|
||||||
|
`launcher -K iidxhook6.dll bm2dx.dll --config iidxhook-20.conf %*`
|
||||||
|
|
||||||
|
- **Spicetools**
|
||||||
|
|
||||||
|
Easiest method is to add "tickerhook.dll" to your spicecfg options, alternatively, add
|
||||||
|
`-k TickerHook.dll` to your start.bat file, making sure the ticker dll is in the game folder
|
||||||
|
|
||||||
## Licensing:
|
## Licensing:
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue