Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

添加音乐和音频类型的html支持 #253

Merged
merged 1 commit into from
Jan 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 42 additions & 3 deletions app/DataBase/output_pc.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
from ..log import logger
from ..person import Me
from ..util import path
from ..util.compress_content import parser_reply
from ..util.compress_content import parser_reply, music_share
from ..util.emoji import get_emoji_url
from ..util.file import get_file
from ..util.music import get_music_path
from ..util.image import get_image_path, get_image, get_image_abs_path

os.makedirs('./data/聊天记录', exist_ok=True)
Expand All @@ -48,11 +49,23 @@ def makedirs(path):
os.makedirs(os.path.join(path, 'voice'), exist_ok=True)
os.makedirs(os.path.join(path, 'file'), exist_ok=True)
os.makedirs(os.path.join(path, 'avatar'), exist_ok=True)
os.makedirs(os.path.join(path, 'music'), exist_ok=True)
os.makedirs(os.path.join(path, 'icon'), exist_ok=True)
file = './app/resources/data/file.png'
if not os.path.exists(file):
resource_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__)))
file = os.path.join(resource_dir, 'app', 'resources', 'data', 'file.png')
shutil.copy(file, path + '/file/file.png')
shutil.copy(file, path + '/icon/file.png')
play_file = './app/resources/data/play.png'
if not os.path.exists(play_file):
resource_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__)))
play_file = os.path.join(resource_dir, 'app', 'resources', 'data', 'play.png')
shutil.copy(play_file, path + '/icon/play.png')
pause_file = './app/resources/data/pause.png'
if not os.path.exists(pause_file):
resource_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__)))
pause_file = os.path.join(resource_dir, 'app', 'resources', 'data', 'pause.png')
shutil.copy(pause_file, path + '/icon/pause.png')


def escape_js_and_html(input_str):
Expand Down Expand Up @@ -437,7 +450,7 @@ def file(self, doc, message):
if self.output_type == Output.HTML:
link = get_file(bytesExtra, thumb=True, output_path=origin_docx_path + '/file')
file_name = ''
file_path = './file/file.png'
file_path = './icon/file.png'
if link != "":
file_name = os.path.basename(link)
link = './file/' + file_name
Expand Down Expand Up @@ -621,6 +634,30 @@ def create_table(self, doc, is_send, avatar_path):
content_cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
return content_cell

def music_share(self, doc, message):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"
is_send = message[4]
timestamp = message[5]
content = music_share(message[11])
music_path = ''
if content.get('audio_url') != '':
music_path = get_music_path(content.get('audio_url'), content.get('title'),
output_path=origin_docx_path + '/music')
if music_path != '':
music_path = f'./music/{os.path.basename(music_path)}'
music_path = music_path.replace('\\', '/')
is_chatroom = 1 if self.contact.is_chatroom else 0
avatar = self.get_avatar_path(is_send, message)
display_name = self.get_display_name(is_send, message)

if self.output_type == Output.HTML:
if content.get('is_error') == False:
doc.write(
f'''{{ type:49, text:'{music_path}',is_send:{is_send},avatar_path:'{avatar}',link_url:'{content.get('link_url')}',
timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}',sub_type:3,title:'{content.get('title')}',
artist:'{content.get('artist')}', website_name:'{content.get('website_name')}'}},'''
)

def to_csv(self):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"
os.makedirs(origin_docx_path, exist_ok=True)
Expand Down Expand Up @@ -693,6 +730,8 @@ def to_html_(self):
self.refermsg(f, message)
elif type_ == 49 and sub_type == 6 and self.message_types.get(4906):
self.file(f, message)
elif type_ == 49 and sub_type == 3 and self.message_types.get(4903):
self.music_share(f, message)
f.write(html_end)
f.close()
self.okSignal.emit(1)
Expand Down
161 changes: 161 additions & 0 deletions app/resources/data/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,86 @@
.chat-file a img,.chat-file div img{
width: 100px;
}

.chat-music-audio {
width: 300px;
margin-right: 20px;
display: flex;
flex-direction: column;
padding: 10px;
background-color: #fff;
border-radius: 4px;
cursor: pointer;
height: 100px;
margin-left: 10px;
}

.chat-music-audio .player-box {
width: 300px;
display: flex;
align-items: center;
cursor: pointer;
height: 80px;
}

.chat-music-audio .player-original {
border-top: 1px solid #ede3e3;
}

.chat-music-audio .player-original p {
margin-top: 5px;
color: #888;
}

.chat-music-audio .player-controls {
display: flex;
align-items: center;;
}

.chat-music-audio .flex1 {
flex: 1;
justify-content: start;
}

.chat-music-audio .flex2 {
flex: 2;
justify-content: end;
}

.chat-music-audio .player-info {
width: 200px;
height: 80px;
white-space: normal;
flex-basis: 200px;
}

.chat-music-audio .song-title {
font-weight: bold;
overflow-wrap: break-word;
}

.chat-music-audio .artist {
margin-top: 10px;
color: #888;
}

.chat-music-audio .play-button {
width: 50px;
height: 50px;
background-color: rgba(0, 0, 0, 0);
border-radius: 50%;
border: none;
outline: none;
cursor: pointer;
}

.chat-music-audio .play-button.playing {
background: url("./icon/pause.png");
}

.chat-music-audio .play-button.paused {
background: url("./icon/play.png");
}
.input-area{
border-top:0.5px solid #e0e0e0;
height: 150px;
Expand Down Expand Up @@ -619,6 +699,53 @@
return messageFileTag;
}

function messageMusicAudioBox(message) {
const messageMusicAudioTag = document.createElement('div');
messageMusicAudioTag.className = `chat-music-audio`;
messageMusicAudioTag.dataset.link = message.link_url;
messageMusicAudioTag.onclick = function (event) {
if (!event.target.classList.contains('play-button')) {
window.open(message.link_url, '_blank');
}
}
if (message.title.length >= 39) {
message.title = message.title.slice(0, 38) + '...'
}
messageMusicAudioTag.innerHTML = `<div class="player-box">
<div class="player-info flex1">
<div class="song-title">${message.title}</div>
</div>
<div class="player-controls flex2">
</div>
</div>
<div class="player-original"><p>${message.website_name}</p></div>

`
if (message.text != '') {
var audio = document.createElement('audio');
audio.src = message.text;
messageMusicAudioTag.querySelector('.player-controls').append(audio)
};
var artist = document.createElement('div');
artist.className = 'artist';
artist.innerHTML = message.artist
if (message.title.length < 26) {
messageMusicAudioTag.querySelector('.player-info').append(artist)
}
var playButton = document.createElement('button');
playButton.className = 'play-button paused';
playButton.onclick = function (event) {
event.stopPropagation(); // 阻止点击播放按钮时触发父级的点击事件
toggleAudio(event.target);
};
if (message.is_send) {
messageMusicAudioTag.querySelector('.player-controls').append(playButton)
} else {
messageMusicAudioTag.querySelector('.player-controls').prepend(playButton)
}
return messageMusicAudioTag;
}

// 从数据列表中取出对应范围的元素并添加到容器中
for (let i = startIndex; i < endIndex && i < chatMessages.length; i++) {
const message = chatMessages[i];
Expand Down Expand Up @@ -721,6 +848,20 @@
messageElement.appendChild(message.is_send ? messageContent : avatarTag);
messageElement.appendChild(message.is_send ? avatarTag : messageContent);
}
if (message.sub_type == 3) {
// displayname 和 file
messageContent.className = `content-wrapper content-wrapper-${side}`;
if (message.is_chatroom && !message.is_send) {
messageContent.appendChild(displayNameBox(message));
}
messageContent.appendChild(messageMusicAudioBox(message));

// 整合
messageElement.className = `item item-${side}`;
messageElement.appendChild(message.is_send ? messageContent : avatarTag);
messageElement.appendChild(message.is_send ? avatarTag : messageContent);
}

}
else if (message.type == 34) {
// displayname 和 转的文字 和 audio
Expand Down Expand Up @@ -844,6 +985,26 @@
var modal = document.getElementById("modal");
modal.style.display = "none";
}


function toggleAudio(buttonElm) {
var audioPlayer = buttonElm.parentNode;
var audio = audioPlayer.querySelector('audio');
if (audio == null){
alert("该音频已失效或无法直接播放,有需要请点击音频链接查看")
}else{
if (audio.paused) {
audio.play();
buttonElm.classList.remove('paused');
buttonElm.classList.add('playing');
} else {
audio.pause();
buttonElm.classList.remove('playing');
buttonElm.classList.add('paused');
}
}

}
</script>
</body>
</html>
5 changes: 3 additions & 2 deletions app/ui/contact/export_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
'语音': 34,
'视频': 43,
'表情包': 47,
'音乐与音频': 4903,
'文件': 4906,
'拍一拍等系统消息': 10000
}
Expand All @@ -33,7 +34,7 @@ def __init__(self, contact=None, title="选择导出的类型", file_type="csv",
if file_type == 'html':
self.export_type = Output.HTML
self.export_choices = {"文本": True, "图片": True, "语音": False, "视频": False, "表情包": False,
'文件': True,
'音乐与音频': False,'文件': False,
'拍一拍等系统消息': True} # 定义导出的数据类型,默认全部选择
elif file_type == 'csv':
self.export_type = Output.CSV
Expand Down Expand Up @@ -64,7 +65,7 @@ def __init__(self, contact=None, title="选择导出的类型", file_type="csv",
layout.addWidget(self.time_label)
self.notice_label = QLabel(self)
self.notice_label.setText(
"注意:导出HTML时选择图片、视频、语音、表情包(特别是表情包)\n会导致大大影响导出速度,请合理选择导出的类型")
"注意:导出HTML时选择图片、视频、语音、文件、音乐与音频、表情包(特别是表情包)\n会导致大大影响导出速度,请合理选择导出的类型")
layout.addWidget(self.notice_label)
hlayout = QHBoxLayout(self)
self.export_button = QPushButton("导出")
Expand Down
86 changes: 86 additions & 0 deletions app/util/compress_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@

import lz4.block

import requests
from urllib.parse import urlparse
from bs4 import BeautifulSoup



def decompress_CompressContent(data):
"""
解压缩Msg:CompressContent内容
Expand Down Expand Up @@ -80,3 +86,83 @@ def parser_reply(data: bytes):
},
"is_error": True
}


def music_share(data: bytes):
xml_content = decompress_CompressContent(data)
if not xml_content:
return {
'type': 3,
'title': "发生错误",
"is_error": True
}
try:
root = ET.XML(xml_content)
appmsg = root.find('appmsg')
msg_type = int(appmsg.find('type').text)
title = appmsg.find('title').text
if len(title) >= 39:
title = title[:38] + '...'
artist = appmsg.find('des').text
link_url = appmsg.find('url').text # 链接地址
audio_url = get_audio_url(appmsg.find('dataurl').text) # 播放地址
website_name = get_website_name(link_url)
return {
'type': msg_type,
'title': title,
'artist': artist,
'link_url': link_url,
'audio_url': audio_url,
'website_name': website_name,
"is_error": False
}
except Exception as e:
print(f"Music Share Error: {e}")
return {
'type': 3,
'title': "发生错误",
"is_error": True
}


def get_website_name(url):
parsed_url = urlparse(url)
domain = f"{parsed_url.scheme}://{parsed_url.netloc}"
website_name = ''
try:
response = requests.get(domain, allow_redirects=False)
if response.status_code == 200:
soup = BeautifulSoup(response.content, 'html.parser')
website_name = soup.title.string.strip()
elif response.status_code == 302:
domain = response.headers['Location']
response = requests.get(domain, allow_redirects=False)
soup = BeautifulSoup(response.content, 'html.parser')
website_name = soup.title.string.strip()
else:
response = requests.get(url, allow_redirects=False)
if response.status_code == 200:
soup = BeautifulSoup(response.content, 'html.parser')
website_name = soup.title.string.strip()
index = website_name.find("-")
if index != -1: # 如果找到了 "-"
website_name = website_name[index+1:].strip()
except Exception as e:
print(f"Get Website Info Error: {e}")
return website_name


def get_audio_url(url):
path = ''
try:
response = requests.get(url, allow_redirects=False)
# 检查响应状态码
if response.status_code == 302:
path = response.headers['Location']
elif response.status_code == 200:
print('音乐文件已失效,url:' + url)
else:
print('音乐文件地址获取失败,url:' + url +',状态码' + str(response.status_code))
except Exception as e:
print(f"Get Audio Url Error: {e}")
return path
Loading