1 Commits
v0.1 ... v0.2

Author SHA1 Message Date
Suxue
ef2024f90e 新增音频文件转码
新增音频导出至html文本
2023-09-07 13:11:16 +08:00
8 changed files with 251 additions and 6 deletions

166
Helpers/ToolsHelper.cs Normal file
View File

@@ -0,0 +1,166 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WechatPCMsgBakTool.Helpers
{
public class ToolsHelper
{
public static TaskFactory factory = new TaskFactory(new LimitedConcurrencyLevelTaskScheduler(10));
public static string DecodeVoice(string source,string pcm,string to)
{
string ffmpeg = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Tools", "ffmpeg.exe");
string silk_decoder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Tools", "silk_v3_decoder.exe");
Task task = factory.StartNew(() =>
{
Process silk = new Process();
silk.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
silk.StartInfo.UseShellExecute = false;
silk.StartInfo.CreateNoWindow = true;
silk.StartInfo.FileName = silk_decoder;
silk.StartInfo.Arguments = string.Format("\"{0}\" \"{1}\"", source, pcm);
silk.Start();
silk.WaitForExit();
if (File.Exists(pcm))
{
Process ff = new Process();
ff.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
ff.StartInfo.UseShellExecute= false;
ff.StartInfo.CreateNoWindow = true;
ff.StartInfo.FileName = ffmpeg;
ff.StartInfo.Arguments = string.Format(" -y -f s16le -ar 24000 -ac 1 -i \"{0}\" -ar 24000 -b:a 320k \"{1}\"", pcm, to);
ff.Start();
ff.WaitForExit();
}
});
return "";
}
}
public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
{
// Indicates whether the current thread is processing work items.
[ThreadStatic]
private static bool _currentThreadIsProcessingItems;
// The list of tasks to be executed
private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); // protected by lock(_tasks)
// The maximum concurrency level allowed by this scheduler.
private readonly int _maxDegreeOfParallelism;
// Indicates whether the scheduler is currently processing work items.
private int _delegatesQueuedOrRunning = 0;
// Creates a new instance with the specified degree of parallelism.
public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism)
{
if (maxDegreeOfParallelism < 1) throw new ArgumentOutOfRangeException("maxDegreeOfParallelism");
_maxDegreeOfParallelism = maxDegreeOfParallelism;
}
// Queues a task to the scheduler.
protected sealed override void QueueTask(Task task)
{
// Add the task to the list of tasks to be processed. If there aren't enough
// delegates currently queued or running to process tasks, schedule another.
lock (_tasks)
{
_tasks.AddLast(task);
if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism)
{
++_delegatesQueuedOrRunning;
NotifyThreadPoolOfPendingWork();
}
}
}
// Inform the ThreadPool that there's work to be executed for this scheduler.
private void NotifyThreadPoolOfPendingWork()
{
ThreadPool.UnsafeQueueUserWorkItem(_ =>
{
// Note that the current thread is now processing work items.
// This is necessary to enable inlining of tasks into this thread.
_currentThreadIsProcessingItems = true;
try
{
// Process all available items in the queue.
while (true)
{
Task item;
lock (_tasks)
{
// When there are no more items to be processed,
// note that we're done processing, and get out.
if (_tasks.Count == 0)
{
--_delegatesQueuedOrRunning;
break;
}
// Get the next item from the queue
item = _tasks.First.Value;
_tasks.RemoveFirst();
}
// Execute the task we pulled out of the queue
base.TryExecuteTask(item);
}
}
// We're done processing items on the current thread
finally { _currentThreadIsProcessingItems = false; }
}, null);
}
// Attempts to execute the specified task on the current thread.
protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
// If this thread isn't already processing a task, we don't support inlining
if (!_currentThreadIsProcessingItems) return false;
// If the task was previously queued, remove it from the queue
if (taskWasPreviouslyQueued)
// Try to run the task.
if (TryDequeue(task))
return base.TryExecuteTask(task);
else
return false;
else
return base.TryExecuteTask(task);
}
// Attempt to remove a previously scheduled task from the scheduler.
protected sealed override bool TryDequeue(Task task)
{
lock (_tasks) return _tasks.Remove(task);
}
// Gets the maximum concurrency level supported by this scheduler.
public sealed override int MaximumConcurrencyLevel { get { return _maxDegreeOfParallelism; } }
// Gets an enumerable of the tasks currently scheduled on this scheduler.
protected sealed override IEnumerable<Task> GetScheduledTasks()
{
bool lockTaken = false;
try
{
Monitor.TryEnter(_tasks, ref lockTaken);
if (lockTaken) return _tasks;
else throw new NotSupportedException();
}
finally
{
if (lockTaken) Monitor.Exit(_tasks);
}
}
}
}

View File

@@ -73,6 +73,16 @@ namespace WechatPCMsgBakTool
}
HtmlBody += string.Format("<p class=\"content\"><video controls style=\"max-height:300px;max-width:300px;\"><source src=\"{0}\" type=\"video/mp4\" /></video></p></div>", path);
}
else if(msg.Type == 34)
{
string? path = reader.GetVoice(msg);
if (path == null)
{
HtmlBody += string.Format("<p class=\"content\">{0}</p></div>", "视频不存在");
continue;
}
HtmlBody += string.Format("<p class=\"content\"><audio controls src=\"{0}\"></audio></p></div>", path);
}
else
{
HtmlBody += string.Format("<p class=\"content\">{0}</p></div>", "暂未支持的消息");

View File

@@ -85,6 +85,14 @@ namespace WechatPCMsgBakTool.Model
public string StrContent { get; set; } = "";
}
[Table("Media")]
public class WXMediaMsg
{
public int Key { get; set; }
public byte[]? Buf { get; set; }
public string Reserved0 { get; set; } = "";
}
[Table("Contact")]
public class WXContact
{

View File

@@ -1,8 +1,8 @@
# WechatPCMsgBakTool
微信PC聊天记录备份工具仅支持Windows
- 支持3.9.6.33版本
- 导出图片、视频
- 当前仅支持3.9.6.33版本,后续将版本文件拆分出来
- 导出图片、视频、音频
- 导出Html文件
本项目仅做学习使用,供个人备份自己的微信,请勿做其他用途使用。
@@ -12,7 +12,7 @@
#### 使用
<p>1.打开微信,并登录。</p>
<p>2.将微信设置内的个人目录,填入用户文件夹的文本框,注意,需要带账号,你从微信设置里面点点打开文件夹那个路径就是对的了。</p>
<p>3.依次点击,确定,解密,读取,即可在左侧见到会话列表了。>3.依次点击,确定,解密,读取,即可在左侧见到会话列表了。</p>
<p>3.依次点击,确定,解密,读取,即可在左侧见到会话列表了。</p>
<p>4.如果会话列表内没有这个人,你可以按账号搜索。</p>
<p>5.如果使用过程中发生崩溃请删除工作区试一下工作区即根据用户名在运行目录下生成的md5文件夹。</p>
<p>6.如果你已经读取过一次数据了,想离线使用,请填入微信个人目录后,选择使用已解密的工作区读取,即可本地离线加载。</p>
@@ -20,6 +20,7 @@
#### 参考/引用
都是站在大佬们的肩膀上完成的项目,本项目 参考/引用 了以下 项目/文章 内代码。
##### [Mr0x01/WXDBDecrypt.NET](https://github.com/Mr0x01/WXDBDecrypt.NET")
##### [AdminTest0/SharpWxDump](https://github.com/AdminTest0/SharpWxDump")
##### [吾爱破解chenhahacjl/微信 DAT 图片解密 C#](https://www.52pojie.cn/forum.php?mod=viewthread&tid=1507922")
##### [Mr0x01/WXDBDecrypt.NET](https://github.com/Mr0x01/WXDBDecrypt.NET)
##### [AdminTest0/SharpWxDump](https://github.com/AdminTest0/SharpWxDump)
##### [kn007/silk-v3-decoder](https://github.com/kn007/silk-v3-decoder)
##### [吾爱破解chenhahacjl/微信 DAT 图片解密 C#](https://www.52pojie.cn/forum.php?mod=viewthread&tid=1507922)

BIN
Tools/ffmpeg.exe Normal file

Binary file not shown.

BIN
Tools/silk_v3_decoder.exe Normal file

Binary file not shown.

View File

@@ -6,6 +6,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Interop;
using WechatPCMsgBakTool.Helpers;
@@ -96,6 +97,22 @@ namespace WechatPCMsgBakTool
return tmp;
}
public WXMediaMsg? GetVoiceMsg(string msgid)
{
for (int i = 0; i <= DecDBInfo.MaxMediaDBCount; i++)
{
SQLiteConnection con = DBInfo["MediaMSG" + i.ToString()];
if (con == null)
continue;
string query = "select * from Media where Reserved0=?";
List<WXMediaMsg> wXMsgs = con.Query<WXMediaMsg>(query, msgid);
if(wXMsgs.Count != 0)
return wXMsgs[0];
}
return null;
}
public string? GetVideo(WXMsg msg)
{
WXSessionAttachInfo? attachInfo = GetWXMsgAtc(msg);
@@ -115,9 +132,43 @@ namespace WechatPCMsgBakTool
if(!File.Exists(savePath))
File.Copy(resBasePath, savePath, false);
savePath = savePath.Replace(DecDBInfo.UserPath + "\\", "");
return savePath;
}
public string? GetVoice(WXMsg msg) {
string tmp = Path.Combine(DecDBInfo.UserPath, msg.StrTalker, "tmp");
if (!Directory.Exists(tmp))
{
Directory.CreateDirectory(tmp);
}
WXMediaMsg? voiceMsg = GetVoiceMsg(msg.MsgSvrID);
if(voiceMsg != null)
{
if (voiceMsg.Buf == null)
return null;
string voicePath = Path.Combine(DecDBInfo.UserPath, msg.StrTalker, "Voice");
if (!Directory.Exists(voicePath))
Directory.CreateDirectory(voicePath);
// 从DB取音频文件到临时目录
string tmp_file_path = Path.Combine(tmp, voiceMsg.Key + ".arm");
using (FileStream stream = new FileStream(tmp_file_path,FileMode.OpenOrCreate))
{
stream.Write(voiceMsg.Buf, 0, voiceMsg.Buf.Length);
}
// 调用silk_v3_decoder解码成pcm
string tmp_pcm_file_path = Path.Combine(tmp, voiceMsg.Key + ".pcm");
// 调用ffmpeg转换成mp3
string mp3_file_path = Path.Combine(voicePath, voiceMsg.Key + ".mp3");
ToolsHelper.DecodeVoice(tmp_file_path, tmp_pcm_file_path, mp3_file_path);
mp3_file_path = mp3_file_path.Replace(DecDBInfo.UserPath + "\\", "");
return mp3_file_path;
}
return null;
}
public string GetSavePath(WXSession session)
{
string savePath = Path.Combine(DecDBInfo.UserPath, session.UserName + ".html");
@@ -147,6 +198,7 @@ namespace WechatPCMsgBakTool
Directory.CreateDirectory(imgPath);
string img = DecImage(resBasePath, imgPath);
img = img.Replace(DecDBInfo.UserPath + "\\", "");
return img;
}

View File

@@ -6,6 +6,8 @@
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<AssemblyVersion>0.2.0.0</AssemblyVersion>
<FileVersion>0.2.0.0</FileVersion>
</PropertyGroup>
<ItemGroup>
@@ -19,6 +21,12 @@
<None Update="libssl-1_1-x64.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Tools\ffmpeg.exe">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Tools\silk_v3_decoder.exe">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>