Compare commits

..

No commits in common. "main" and "main" have entirely different histories.
main ... main

12 changed files with 190 additions and 393 deletions

Binary file not shown.

View file

@ -1,5 +1 @@
path: C:\Your\Path\Here path: E:\autorecording
resolution: 1920x1080
framerate: 60
crf: 20
game_process_name: spice64

View file

@ -1,75 +1,39 @@
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 = GetDefaultVideosFolderPath(); private static string _ffmpegFolderPath = GetFolderPath();
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)
{ {
DownloadFFmpeg(); var spiceProcesses = Process.GetProcessesByName("spice64");
LoadSettingsFromPropFile();
_encoder = GetHardwareEncoder(); if (spiceProcesses.Length > 0)
var gameProcesses = Process.GetProcessesByName(_gameProcessName);
if (gameProcesses.Length > 0)
{ {
Console.WriteLine($"Found {_gameProcessName}, Attempting connection to TickerHookWS..."); Console.WriteLine("Found spice64, Attempting connection to TickerHookWS...");
await TryConnectWebSocket(); await TryConnectWebSocket();
} }
else else
{ {
Console.WriteLine($"Unable to find {_gameProcessName}. Is the game running and TickerHook enabled?"); Console.WriteLine("Unable to find Spice64. Are you sure Beatmania IIDX is running and TickerHook is 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))
@ -83,153 +47,142 @@ class Program
var path = line["path:".Length..].Trim(); var path = line["path:".Length..].Trim();
if (Directory.Exists(path)) if (Directory.Exists(path))
{ {
_ffmpegFolderPath = path; return path;
} }
else Console.WriteLine($"The path specified in {filePath} does not exist.");
{
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. Using default values."); Console.WriteLine($"The file {filePath} does not exist.");
_ffmpegFolderPath = GetDefaultVideosFolderPath();
} }
return GetDefaultVideosFolderPath();
} }
private static string GetDefaultVideosFolderPath() private static string GetDefaultVideosFolderPath()
{ {
var userName = Environment.UserName; var userName = Environment.UserName;
return Path.Combine($@"C:\Users\{userName}", "Videos"); var defaultPath = 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)
{ {
const int maxRetries = 5; Console.WriteLine($"Attempt {attempt + 1} of {maxRetries} to connect...");
var attempt = 0;
while (attempt < maxRetries) bool success = await ConnectWebSocket();
if (success)
{ {
Console.WriteLine($"Attempt {attempt + 1} of {maxRetries} to connect..."); break; // Exit the loop if connection was successful
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("Failed to connect after 5 attempts."); Console.WriteLine($"Retrying in 10 seconds... {maxRetries - attempt} attempts remaining.");
await Task.Delay(10000); // Wait for 10 seconds before retrying
} }
} }
private static async Task<bool> ConnectWebSocket() if (attempt == maxRetries)
{ {
var tickerUri = new Uri($"ws://{WebsocketAddress}:10573"); Console.WriteLine("Failed to connect after 5 attempts.");
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(); private static async Task<bool> ConnectWebSocket()
{
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
{ {
await clientWebSocket.ConnectAsync(tickerUri, CancellationToken.None); result = await clientWebSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
Console.WriteLine("Connected to TickerHook WebSocket.");
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Error connecting to TickerHook WebSocket: {ex.Message}"); Console.WriteLine($"Error receiving message: {ex.Message}");
return false; reconnecting = true;
break;
} }
var buffer = new byte[1024]; var message = Encoding.UTF8.GetString(buffer, 0, result.Count).Trim().ToUpper();
while (clientWebSocket.State == WebSocketState.Open) Console.WriteLine($"Received message: {message}");
if (message == lastMessage && !message.Contains("SELECT FROM"))
{ {
WebSocketReceiveResult result; consecutiveMessageCount++;
try }
{ else
result = await clientWebSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); {
} consecutiveMessageCount = 1;
catch (Exception ex) lastMessage = message;
{
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; 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;
}
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)
@ -238,7 +191,8 @@ class Program
{ {
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";
Directory.CreateDirectory(Path.GetDirectoryName(_audioFilePath)!); var outputFolder = 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) =>
@ -248,8 +202,11 @@ class Program
_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.");
} }
@ -259,153 +216,70 @@ class Program
} }
} }
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} " +
$"-filter_complex \"ddagrab=framerate={_framerate},hwdownload,format=bgra\" " + var ffmpegArguments = $"-framerate 60 " +
$"{_encoder} -crf {_crf} -video_size {_resolution} -draw_mouse 0 -movflags +faststart -y \"{_videoFilePath}\""; $"-filter_complex \"ddagrab=0,hwdownload,format=bgra\" " +
_ffmpegProcess = new Process $"-c:v libx264 -movflags +faststart -crf 20 -y \"{_videoFilePath}\"";
{
StartInfo = new ProcessStartInfo _ffmpegProcess = new Process();
{ _ffmpegProcess.StartInfo.FileName = "ffmpeg";
FileName = "ffmpeg", _ffmpegProcess.StartInfo.Arguments = ffmpegArguments;
Arguments = ffmpegArguments, _ffmpegProcess.StartInfo.UseShellExecute = false;
UseShellExecute = false, _ffmpegProcess.StartInfo.RedirectStandardOutput = false;
RedirectStandardError = true, _ffmpegProcess.StartInfo.RedirectStandardError = true;
CreateNoWindow = true _ffmpegProcess.StartInfo.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)
{ {
StopAudioRecording();
StopFfmpegRecording(); StopFfmpegRecording();
StopAudioRecording();
CombineAudioAndVideo(_videoFilePath, _audioFilePath, songName); CombineAudioAndVideo(_videoFilePath, _audioFilePath, songName);
} }
private static void StopAudioRecording() private static void StopAudioRecording()
{ {
_waveSource?.StopRecording(); if (_waveSource == null) return;
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) if (_ffmpegProcess != null && _ffmpegProcess.HasExited) return;
{ _ffmpegProcess?.Kill();
_ffmpegProcess.Kill(); _ffmpegProcess?.WaitForExit();
_ffmpegProcess.WaitForExit(); _ffmpegProcess?.Dispose();
_ffmpegProcess = null!; _ffmpegProcess = null;
Console.WriteLine("FFmpeg recording stopped."); Console.WriteLine("FFMPEG process 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 -shortest \"{combinedOutputFilePath}\""; $"-y -i \"{videoFilePath}\" -i \"{audioFilePath}\" -c:v copy -c:a aac -strict experimental \"{combinedOutputFilePath}\"";
var processInfo = new ProcessStartInfo var processInfo = new ProcessStartInfo
{ {
@ -436,4 +310,4 @@ class Program
File.Delete(videoFilePath); File.Delete(videoFilePath);
File.Delete(audioFilePath); File.Delete(audioFilePath);
} }
} }

View file

@ -1,32 +1,25 @@
{ {
"format": 1, "format": 1,
"restore": { "restore": {
"E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj": {} "D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj": {}
}, },
"projects": { "projects": {
"E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj": { "D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj": {
"version": "1.0.0", "version": "1.0.0",
"restore": { "restore": {
"projectUniqueName": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj", "projectUniqueName": "D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
"projectName": "2dxAutoClip", "projectName": "2dxAutoClip",
"projectPath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj", "projectPath": "D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
"packagesPath": "C:\\Users\\Mercury\\.nuget\\packages\\", "packagesPath": "C:\\Users\\Mercury\\.nuget\\packages\\",
"outputPath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\obj\\", "outputPath": "D:\\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": {
@ -39,6 +32,11 @@
"warnAsError": [ "warnAsError": [
"NU1605" "NU1605"
] ]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
} }
}, },
"frameworks": { "frameworks": {
@ -70,7 +68,7 @@
"privateAssets": "all" "privateAssets": "all"
} }
}, },
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\8.0.300/PortableRuntimeIdentifierGraph.json" "runtimeIdentifierGraphPath": "C:\\Users\\Mercury\\.dotnet\\sdk\\8.0.303/PortableRuntimeIdentifierGraph.json"
} }
} }
} }

View file

@ -5,12 +5,11 @@
<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\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders> <NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\Mercury\.nuget\packages\</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle> <NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.9.1</NuGetToolVersion> <NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.10.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>

View file

@ -452,32 +452,24 @@
] ]
}, },
"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": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj", "projectUniqueName": "D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
"projectName": "2dxAutoClip", "projectName": "2dxAutoClip",
"projectPath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj", "projectPath": "D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
"packagesPath": "C:\\Users\\Mercury\\.nuget\\packages\\", "packagesPath": "C:\\Users\\Mercury\\.nuget\\packages\\",
"outputPath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\obj\\", "outputPath": "D:\\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": {
@ -490,6 +482,11 @@
"warnAsError": [ "warnAsError": [
"NU1605" "NU1605"
] ]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
} }
}, },
"frameworks": { "frameworks": {
@ -521,7 +518,7 @@
"privateAssets": "all" "privateAssets": "all"
} }
}, },
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\8.0.300/PortableRuntimeIdentifierGraph.json" "runtimeIdentifierGraphPath": "C:\\Users\\Mercury\\.dotnet\\sdk\\8.0.303/PortableRuntimeIdentifierGraph.json"
} }
} }
} }

View file

@ -1,8 +1,8 @@
{ {
"version": 2, "version": 2,
"dgSpecHash": "xTfdkrpqvxmeY+pzZcnpBIJbSyoO94xa04eSLg8IlKmzbTfi/q7IfP/sLGg2XwIVxlpAVQKwcAeegUkFcOHA5A==", "dgSpecHash": "71ChNRMXyyA=",
"success": true, "success": true,
"projectFilePath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj", "projectFilePath": "D:\\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",

View file

@ -1 +1 @@
"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"}} "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"}}

View file

@ -1 +1 @@
17259084205730795 17255542395188510

View file

@ -1 +1 @@
17259084285638427 17255543032402240

View file

@ -1,40 +0,0 @@
# 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.

View file

@ -1,43 +1,16 @@
# 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. Downloads FFMPEG on startup. 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.
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 Settings: ## Custom Recording Path:
Version 0.0.4 and onwards will allow for some flexibility in the 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.
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 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 - .net 8 if you'd rather build the app on your own
## 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: