This commit is contained in:
恍兮惚兮 2024-12-30 14:15:24 +08:00
parent d06c29fc59
commit 43349deec5
48 changed files with 296 additions and 343 deletions

View File

@ -268,6 +268,9 @@ def downloadbass():
"https://www.un4seen.com/files/z/2/bass_spx24.zip",
"https://www.un4seen.com/files/z/2/bass_aac24.zip",
"https://www.un4seen.com/files/bassopus24.zip",
"https://www.un4seen.com/files/bassenc24.zip",
"https://www.un4seen.com/files/bassenc_mp324.zip",
"https://www.un4seen.com/files/bassenc_opus24.zip",
):
name = link.split("/")[-1]
d = name.split(".")[0]

1
.gitignore vendored
View File

@ -37,6 +37,7 @@ cpp/LunaHook/builds
cpp/builds
cpp/libs/webview2
cpp/.vscode/settings.json
cpp/libs/bass
cpp/libs/opencv-static/windows-x86
cpp/libs/opencv-static/windows-x64
cpp/libs/onnxruntime-static/windows-x86

3
.gitmodules vendored
View File

@ -7,9 +7,6 @@
[submodule "src/plugins/libs/wechat-ocr"]
path = cpp/libs/wechat-ocr
url = https://github.com/swigger/wechat-ocr
[submodule "src/plugins/libs/tinymp3"]
path = cpp/libs/tinymp3
url = https://github.com/HIllya51/tinymp3
[submodule "src/plugins/libs/Clipper2"]
path = cpp/libs/Clipper2
url = https://github.com/AngusJohnson/Clipper2

View File

@ -224,10 +224,6 @@ int PyStand::DetectScript()
}
SetEnvironmentVariableW(L"PYSTAND_SCRIPT", _script.c_str());
std::vector<wchar_t> buffer(MAX_PATH);
GetModuleFileNameW(GetModuleHandle(0), buffer.data(), MAX_PATH);
SetEnvironmentVariableW(L"LUNA_EXE_NAME", buffer.data());
return 0;
}

View File

@ -12,7 +12,6 @@ else()
add_library(wil INTERFACE)
target_include_directories(wil INTERFACE ${CMAKE_CURRENT_LIST_DIR}/wil/include)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/tinymp3 ${CMAKE_BINARY_DIR}/tinymp3)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/rapidfuzz-cpp ${CMAKE_BINARY_DIR}/rapidfuzz-cpp)

@ -1 +0,0 @@
Subproject commit 579726185979f877f2672b956b23538ff00c08ba

View File

@ -1,7 +1,7 @@
set(VERSION_MAJOR 6)
set(VERSION_MINOR 16)
set(VERSION_PATCH 5)
set(VERSION_PATCH 6)
set(VERSION_REVISION 0)
set(LUNA_VERSION "{${VERSION_MAJOR},${VERSION_MINOR},${VERSION_PATCH},${VERSION_REVISION}}")
add_library(VERSION_DEF ${CMAKE_CURRENT_LIST_DIR}/version_def.cpp)

View File

@ -10,16 +10,15 @@ generate_product_version(
VERSION_MINOR ${VERSION_MINOR}
VERSION_PATCH ${VERSION_PATCH}
)
add_library(winsharedutils MODULE mp3enc.cpp webview2_extra.cpp AreoAcrylic.cpp screenshot.cpp ../implsapi.cpp hwnd.cpp globalmessagelistener.cpp theme.cpp version.cpp otsu.cpp clipboard.cpp lnk.cpp levenshtein.cpp muteprocess.cpp sapi_dll.cpp simplemecab.cpp
add_library(winsharedutils MODULE webview2_extra.cpp AreoAcrylic.cpp screenshot.cpp ../implsapi.cpp hwnd.cpp globalmessagelistener.cpp theme.cpp version.cpp otsu.cpp clipboard.cpp lnk.cpp levenshtein.cpp muteprocess.cpp sapi_dll.cpp simplemecab.cpp
applicationloopbackaudio/runer.cpp applicationloopbackaudio/LoopbackCapture.cpp
SimpleBrowser.cpp MWebBrowser.cpp icon.cpp ${versioninfo})
target_precompile_headers(winsharedutils REUSE_FROM pch)
if(NOT WINXP)
target_link_libraries(winsharedutils tinymp3 rapidfuzz wil webview2 Mfplat mfuuid Mmdevapi dwmapi)
target_link_libraries(winsharedutils rapidfuzz wil webview2 Mfplat mfuuid Mmdevapi dwmapi)
target_link_options(winsharedutils PRIVATE "/DELAYLOAD:Mmdevapi.dll")
target_link_options(winsharedutils PRIVATE "/DELAYLOAD:Mfplat.dll")
else()
target_link_libraries(winsharedutils tinymp3)
endif()
if(${CMAKE_SIZEOF_VOID_P} EQUAL 8)
set_target_properties(winsharedutils PROPERTIES OUTPUT_NAME "winsharedutils64")

View File

@ -1,82 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <timing.h>
#include <shine_mp3.h>
#define DR_WAV_IMPLEMENTATION
#include "dr_wav.h"
int stereo = STEREO;
DECLARE_API void encodemp3(void *ptr, size_t size, void (*cb)(void *ptr, size_t size), int bitrate)
{
shine_config_t config;
shine_t s;
int written;
unsigned char *data;
/* Set the default MPEG encoding paramters - basically init the struct */
shine_set_config_mpeg_defaults(&config.mpeg);
config.mpeg.bitr = bitrate; // 8-320;
uint32_t sampleRate = 0;
uint64_t totalSampleCount = 0;
uint32_t channels = 0;
int16_t *data_in = drwav_open_memory_and_read_pcm_frames_s16(ptr, size, &channels, &sampleRate, &totalSampleCount, NULL);
if (data_in == NULL)
return;
totalSampleCount *= channels;
double startTime = now();
config.wave.samplerate = sampleRate;
config.wave.channels = (decltype(config.wave.channels))channels;
/* See if samplerate and bitrate are valid */
if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0)
return;
// printf("Unsupported samplerate/bitrate configuration.");
/* Set to stereo mode if wave data is stereo, mono otherwise. */
if (config.wave.channels > 1)
config.mpeg.mode = (decltype(config.mpeg.mode))stereo;
else
config.mpeg.mode = MONO;
/* Initiate encoder */
s = shine_initialise(&config);
int samples_per_pass = shine_samples_per_pass(s) * channels;
std::string sdata;
/* All the magic happens here */
size_t count = totalSampleCount / samples_per_pass;
int16_t *buffer = data_in;
for (int i = 0; i < count; i++)
{
data = shine_encode_buffer_interleaved(s, buffer, &written);
sdata += std::string((char *)data, written);
buffer += samples_per_pass;
}
size_t last = totalSampleCount % samples_per_pass;
if (last != 0)
{
int16_t *cache = (int16_t *)calloc(samples_per_pass, sizeof(int16_t));
if (cache != NULL)
{
memcpy(cache, buffer, last * sizeof(int16_t));
data = shine_encode_buffer_interleaved(s, cache, &written);
free(cache);
sdata += std::string((char *)data, written);
}
}
/* Flush and write remaining data. */
data = shine_flush(s, &written);
sdata += std::string((char *)data, written);
/* Close encoder. */
shine_close(s);
free(data_in);
double time_interval = calcElapsed(startTime, now());
cb(sdata.data(), sdata.size());
}

View File

@ -16,11 +16,16 @@ class goo(cishubase):
xx = "".join(xx).replace('href="/', 'href="https://dictionary.goo.ne.jp/')
if not self.style:
self.style = requests.get(
"https://dictionary.goo.ne.jp/mix/css/app.css", proxies=self.proxy
).text
self.style = (
requests.get(
"https://dictionary.goo.ne.jp/mix/css/app.css", proxies=self.proxy
)
.text.replace("width:1004px", "")
.replace("width:1024px", "")
.replace("width:644px", "")
)
if len(xx):
return '<div style="text-align: center;"><a href="{}">link</a><style>{}</style></div><div id="NR-main-in">{}</div>'.format(
return '<div style="text-align: center;"><a href="{}">link</a><style>{}</style></div><div id="NR-wrapper"><div id="NR-wrapper-in" class="cx">{}</div></div>'.format(
url, self.style, xx
)

View File

@ -1,8 +1,8 @@
import math, base64, uuid, gobject
from cishu.cishubase import DictTree
from myutils.config import isascii
from myutils.config import isascii, globalconfig
from traceback import print_exc
from myutils.audioplayer import bass_decode
from myutils.audioplayer import bass_code_cast
import json, os
cachejson = None
@ -2293,18 +2293,14 @@ class mdict(cishubase):
file_content = self.parse_url_in_mdd(index, url[8:])
if not file_content:
return
ext = os.path.splitext(url)[1].lower()
if ext in (".aac", ".spx", ".opus"):
mp3 = bass_decode(file_content, ext)
if not mp3:
print(ext, "decode error")
return
file_content = mp3
ext = ".mp3"
ext = os.path.splitext(url)[1].lower()[1:]
if True: # ext in ("aac", "spx", "opus"):
new, ext = bass_code_cast(file_content, fr=ext)
file_content = new
varname = "var_" + hashlib.md5(file_content).hexdigest()
audiob64vals[varname] = base64.b64encode(file_content).decode()
return 3, "javascript:mdict_play_sound('{}',{})".format(
query_mime(ext), varname
query_mime("." + ext), varname
)
file_content = self.parse_url_in_mdd(index, url)
if not file_content:
@ -2515,7 +2511,7 @@ class mdict(cishubase):
)
)
idx += 1
commonstyle = """
res = """
<script>
function onclickbtn_mdict_internal(_id) {
tabPanes = document.querySelectorAll('.tab-widget_mdict_internal .tab-pane_mdict_internal');
@ -2562,12 +2558,7 @@ function onclickbtn_mdict_internal(_id) {
display: block;
}
</style>
"""
res = """
{commonstyle}
<div class="tab-widget_mdict_internal">
<div class="centerdiv_mdict_internal"><div>
{btns}
</div>
@ -2579,7 +2570,7 @@ function onclickbtn_mdict_internal(_id) {
</div>
</div>
""".format(
commonstyle=commonstyle, btns="".join(btns), contents="".join(contents)
btns="".join(btns), contents="".join(contents)
)
return res
@ -2601,8 +2592,7 @@ function onclickbtn_mdict_internal(_id) {
display: none;
padding: 10px;
border: 1px solid #ddd;
}</style>"""
content += """
}</style>
<script>
function mdict_flowstyle_clickcallback(_id)
{
@ -2657,6 +2647,12 @@ for(let i=0;i<elements.length;i++)
}
var lastmusicplayer=false;
function mdict_play_sound(ext, b64){
if(window.mdict_audio_call)
window.mdict_audio_call(b64)
else if(window.LUNAJSObject)
window.LUNAJSObject.mdict_audio_call(b64)
else{
const music = new Audio();
music.src="data:"+ext+";base64,"+b64
if(lastmusicplayer!=false)
@ -2665,6 +2661,7 @@ function mdict_play_sound(ext, b64){
}
lastmusicplayer=music
music.play();
}
}
function safe_mdict_entry_call(word){
if(window.mdict_entry_call)

View File

@ -1,5 +1,3 @@
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget
from qtsymbols import *
import os, functools, uuid
from traceback import print_exc
@ -26,6 +24,7 @@ from gui.usefulwidget import (
IconButton,
statusbutton,
getsimplecombobox,
threeswitch,
FQLineEdit,
FocusCombo,
)
@ -50,30 +49,20 @@ from gui.dialog_savedgame_common import (
class dialog_savedgame_integrated(saveposwindow):
def selectlayout(self, type):
self.syssettingbtn.setVisible(type != 2)
self.syssettingbtn.setVisible(type != 0)
try:
globalconfig["gamemanager_integrated_internal_layout"] = type
klass = [
dialog_savedgame_new,
dialog_savedgame_v3,
dialog_savedgame_legacy,
dialog_savedgame_v3,
dialog_savedgame_new,
][type]
btns = [self.layout1btn, self.layout2btn, self.layout3btn]
btns[(type + 0) % 3].setEnabled(False)
btns[(type + 1) % 3].setEnabled(False)
btns[(type + 2) % 3].setEnabled(False)
btns[(type + 0) % 3].setChecked(True)
btns[(type + 1) % 3].setChecked(False)
btns[(type + 2) % 3].setChecked(False)
_old = self.internallayout.takeAt(0).widget()
_old.hide()
_ = klass(self)
self.internallayout.addWidget(_)
_.directshow()
_old.deleteLater()
btns[(type + 0) % 3].setEnabled(False)
btns[(type + 1) % 3].setEnabled(True)
btns[(type + 2) % 3].setEnabled(True)
self.__internal = _
except:
print_exc()
@ -95,25 +84,13 @@ class dialog_savedgame_integrated(saveposwindow):
self.internallayout.addWidget(QWidget())
self.setCentralWidget(w)
def createbtn(icon, i):
btn = statusbutton(
p=self,
icons=icon,
border=False,
colors=["", globalconfig["buttoncolor2"]],
)
btn.clicked.connect(functools.partial(self.selectlayout, i))
btn.setFixedSize(QSize(20, 25))
return btn
self.layout1btn = createbtn("fa.th", 0)
self.layout2btn = createbtn("fa.th-list", 1)
self.layout3btn = createbtn("fa.list", 2)
self.switch = threeswitch(self, icons=["fa.list", "fa.th-list", "fa.th"])
self.switch.btnclicked.connect(self.selectlayout)
self.syssettingbtn = IconButton(icon="fa.gear", parent=self)
self.syssettingbtn.setFixedSize(QSize(25, 25))
self.syssettingbtn.clicked.connect(self.syssetting)
self.show()
self.selectlayout(globalconfig["gamemanager_integrated_internal_layout"])
self.switch.selectlayout(globalconfig["gamemanager_integrated_internal_layout"])
def syssetting(self):
dialog_syssetting(
@ -122,25 +99,10 @@ class dialog_savedgame_integrated(saveposwindow):
)
def resizeEvent(self, e: QResizeEvent):
self.layout1btn.move(e.size().width() - self.layout1btn.width(), 0)
self.layout2btn.move(
e.size().width() - self.layout2btn.width() - self.layout1btn.width(), 0
)
self.layout3btn.move(
e.size().width()
- self.layout3btn.width()
- self.layout2btn.width()
- self.layout1btn.width(),
0,
)
self.syssettingbtn.move(
e.size().width()
- self.syssettingbtn.width()
- self.layout3btn.width()
- self.layout2btn.width()
- self.layout1btn.width(),
0,
)
x = e.size().width() - self.switch.width()
self.switch.move(x, 0)
x -= self.syssettingbtn.width()
self.syssettingbtn.move(x, 0)
class TagWidget(QWidget):
@ -801,7 +763,7 @@ class dialog_savedgame_new(QWidget):
game2 = self.idxsave[idx2]
self.idxsave.insert(idx2, self.idxsave.pop(idx1))
self.flow.switchidx(idx1, idx2)
try:
self.flow.ensureWidgetVisible(self.flow.widget(idx2))
except:

View File

@ -33,7 +33,7 @@ from gui.usefulwidget import (
def showcountgame(window, num):
if num:
window.setWindowTitle("游戏管理____-_" + str(num))
window.setWindowTitle("游戏管理__-__" + str(num))
else:
window.setWindowTitle("游戏管理")

View File

@ -1,4 +1,3 @@
from PyQt5.QtWidgets import QWidget
from qtsymbols import *
import functools, threading
from myutils.config import savehook_new_list, savehook_new_data, get_launchpath

View File

@ -1,5 +1,3 @@
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget
from qtsymbols import *
import functools, uuid
from datetime import datetime, timedelta

View File

@ -306,15 +306,6 @@ def mainuisetting(self):
),
],
[
"圆角_半径",
D_getspinbox(
0,
100,
globalconfig,
"yuanjiao_r",
callback=lambda _: gobject.baseobject.translation_ui.set_color_transparency(),
),
"",
"可选取的",
D_getsimpleswitch(
globalconfig,
@ -325,6 +316,15 @@ def mainuisetting(self):
parent=self,
name="selectable_btn",
),
"",
"圆角_半径",
D_getspinbox(
0,
100,
globalconfig,
"yuanjiao_r",
callback=lambda _: gobject.baseobject.translation_ui.set_color_transparency(),
),
],
),
),

View File

@ -34,6 +34,7 @@ from gui.usefulwidget import (
getIconButton,
saveposwindow,
tabadd_lazy,
VisLFormLayout,
)
from gui.dynalang import (
LPushButton,
@ -45,6 +46,7 @@ from gui.dynalang import (
LMainWindow,
LAction,
)
from myutils.audioplayer import bass_code_cast
def getimageformat():
@ -62,7 +64,8 @@ class AnkiWindow(QWidget):
def callbacktts(self, edit, sig, data):
if sig != edit.sig:
return
fname = gobject.gettempdir(str(uuid.uuid4()) + ".mp3")
data, ext = bass_code_cast(data, "mp3")
fname = gobject.gettempdir(str(uuid.uuid4()) + "." + ext)
with open(fname, "wb") as ff:
ff.write(data)
self.settextsignal.emit(edit, os.path.abspath(fname))
@ -330,7 +333,7 @@ class AnkiWindow(QWidget):
ff.write(model_css)
def creatsetdtab(self, baselay):
layout = LFormLayout()
layout = VisLFormLayout()
wid = QWidget()
wid.setLayout(layout)
baselay.addWidget(wid)
@ -384,6 +387,39 @@ class AnkiWindow(QWidget):
"成功添加后关闭窗口",
getsimpleswitch(globalconfig["ankiconnect"], "addsuccautoclose"),
)
cnt = layout.rowCount() + 1
def __(xx):
i = ["mp3", "opus"].index(xx)
layout.setRowVisible(cnt + 0, False)
layout.setRowVisible(cnt + 1, False)
layout.setRowVisible(cnt + i, True)
layout.addRow(
"音频编码",
getsimplecombobox(
["mp3", "opus(ogg)"],
globalconfig,
"audioformat",
internal=["mp3", "opus"],
callback=__,
),
)
layout.addRow(
"MP3 kbps",
getsimplecombobox(
[str(8 * i) for i in range(1, 320 // 8 + 1)],
globalconfig,
"mp3kbps",
internal=[8 * i for i in range(1, 320 // 8 + 1)],
),
)
layout.addRow(
"OPUS bitrate",
getspinbox(6, 256, globalconfig, "opusbitrate"),
)
__(globalconfig["audioformat"])
def vistranslate_rank(self):
listediter(
@ -1208,8 +1244,13 @@ class searchwordW(closeashidewindow):
self.textOutput.bind(
"mdict_entry_call", lambda word: self.search_word.emit(word, False)
)
self.textOutput.bind(
"mdict_audio_call",
lambda b64: gobject.baseobject.audioplayer.play(
base64.b64decode(b64.encode()), force=True
),
)
self.cache_results = {}
self.hiding = True
self.spliter = QSplitter()

View File

@ -39,7 +39,6 @@ class transhist(closeashidewindow):
self.textOutput = textOutput
self.setCentralWidget(self.textOutput)
self.hiding = True
def showmenu(self, tb, p):
menu = QMenu(self)

View File

@ -48,7 +48,7 @@ class ButtonX(QWidget):
super().__init__(*argc)
self.reflayout = None
self.setMouseTracking(True)
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
def showinlayout(self, layout):

View File

@ -1617,6 +1617,43 @@ class auto_select_webview(QWidget):
return browser
class threeswitch(QWidget):
btnclicked = pyqtSignal(int)
def selectlayout(self, i):
self.btns[(i + 0) % 3].setEnabled(False)
self.btns[(i + 1) % 3].setEnabled(False)
self.btns[(i + 2) % 3].setEnabled(False)
self.btns[(i + 0) % 3].setChecked(True)
self.btns[(i + 1) % 3].setChecked(False)
self.btns[(i + 2) % 3].setChecked(False)
self.btnclicked.emit(i)
self.btns[(i + 1) % 3].setEnabled(True)
self.btns[(i + 2) % 3].setEnabled(True)
def __init__(self, p, icons):
super().__init__(p)
self.btns = []
for i, icon in enumerate(icons):
btn = statusbutton(
p=self,
icons=icon,
border=False,
colors=["", globalconfig["buttoncolor2"]],
)
btn.clicked.connect(functools.partial(self.selectlayout, i))
btn.setFixedSize(QSize(20, 25))
self.btns.append(btn)
self.setFixedSize(QSize(60, 75))
def resizeEvent(self, a0):
x, y = 0, 0
for btn in self.btns:
btn.move(x, y)
x += btn.width()
return super().resizeEvent(a0)
class threebuttons(QWidget):
btn1clicked = pyqtSignal()
btn2clicked = pyqtSignal()

View File

@ -1,32 +1,31 @@
import time
from traceback import print_exc
from myutils.config import globalconfig
import threading
import gobject, winsharedutils
from ctypes.wintypes import BOOL, DWORD, HWND, WORD
import threading, functools
import gobject
from ctypes.wintypes import BOOL, DWORD, HWND
from ctypes import (
WinDLL,
WINFUNCTYPE,
c_int,
c_ulong,
c_float,
c_int64,
c_void_p,
c_char_p,
Structure,
POINTER,
pointer,
c_void_p,
create_string_buffer,
sizeof,
cast,
c_wchar_p,
c_char,
)
HMUSIC = c_ulong # MOD music handle
HSAMPLE = c_ulong # sample handle
HPLUGIN = c_ulong # Plugin handle
HENCODE = DWORD
HMUSIC = DWORD # MOD music handle
HSAMPLE = DWORD # sample handle
HPLUGIN = DWORD # Plugin handle
QWORD = c_int64
HSTREAM = c_ulong # sample stream handle
HSTREAM = DWORD # sample stream handle
BASS_SAMPLE_FLOAT = 0x100
BASS_STREAM_DECODE = 0x200000
BASS_UNICODE = 0x80000000 # -2147483648
@ -34,63 +33,35 @@ BASS_ATTRIB_VOL = 2
BASS_ATTRIB_FREQ = 1
BASS_SAMPLE_8BITS = 1
BASS_POS_BYTE = 0 # byte position
bass_module = WinDLL(gobject.GetDllpath("bass.dll"))
bass = WinDLL(gobject.GetDllpath("bass.dll"))
BASS_ChannelSetAttribute = WINFUNCTYPE(BOOL, DWORD, DWORD, c_float)(
("BASS_ChannelSetAttribute", bass_module)
("BASS_ChannelSetAttribute", bass)
)
BASS_ChannelGetLength = WINFUNCTYPE(QWORD, DWORD, DWORD)(
("BASS_ChannelGetLength", bass_module)
("BASS_ChannelGetLength", bass)
)
BASS_ChannelGetPosition = WINFUNCTYPE(QWORD, DWORD, DWORD)(
("BASS_ChannelGetPosition", bass_module)
)
BASS_ChannelPlay = WINFUNCTYPE(BOOL, DWORD, BOOL)(("BASS_ChannelPlay", bass_module))
BASS_StreamFree = WINFUNCTYPE(BOOL, HSTREAM)(("BASS_StreamFree", bass_module))
BASS_Init = WINFUNCTYPE(BOOL, c_int, DWORD, DWORD, HWND, c_void_p)(
("BASS_Init", bass_module)
("BASS_ChannelGetPosition", bass)
)
BASS_ChannelPlay = WINFUNCTYPE(BOOL, DWORD, BOOL)(("BASS_ChannelPlay", bass))
BASS_StreamFree = WINFUNCTYPE(BOOL, HSTREAM)(("BASS_StreamFree", bass))
BASS_Init = WINFUNCTYPE(BOOL, c_int, DWORD, DWORD, HWND, c_void_p)(("BASS_Init", bass))
BASS_StreamCreateFile = WINFUNCTYPE(HSTREAM, BOOL, c_void_p, QWORD, QWORD, DWORD)(
("BASS_StreamCreateFile", bass_module)
)
BASS_Free = WINFUNCTYPE(BOOL)(("BASS_Free", bass_module))
BASS_PluginLoad = WINFUNCTYPE(c_ulong, c_char_p, c_ulong)(
("BASS_PluginLoad", bass_module)
("BASS_StreamCreateFile", bass)
)
BASS_Free = WINFUNCTYPE(BOOL)(("BASS_Free", bass))
BASS_PluginLoad = WINFUNCTYPE(HPLUGIN, c_char_p, DWORD)(("BASS_PluginLoad", bass))
bassenc = WinDLL(gobject.GetDllpath("bassenc.dll"))
class WAVEFORMATEX(Structure):
_fields_ = [
("wFormatTag", WORD),
("nChannels", WORD),
("nSamplesPerSec", DWORD),
("nAvgBytesPerSec", DWORD),
("nBlockAlign", WORD),
("wBitsPerSample", WORD),
("cbSize", WORD),
]
BASS_Encode_IsActive = WINFUNCTYPE(DWORD, DWORD)(("BASS_Encode_IsActive", bassenc))
BASS_Encode_Stop = WINFUNCTYPE(BOOL, DWORD)(("BASS_Encode_Stop", bassenc))
class BASS_CHANNELINFO(Structure):
_fields_ = [
("freq", DWORD),
("chans", DWORD),
("flags", DWORD),
("ctype", DWORD),
("origres", DWORD),
("plugin", HPLUGIN),
("sample", HSAMPLE),
("filename", c_char_p),
]
BASS_ChannelGetInfo = WINFUNCTYPE(BOOL, DWORD, POINTER(BASS_CHANNELINFO))(
("BASS_ChannelGetInfo", bass_module)
)
BASS_ChannelIsActive = WINFUNCTYPE(DWORD, DWORD)(("BASS_ChannelIsActive", bass_module))
BASS_ChannelIsActive = WINFUNCTYPE(DWORD, DWORD)(("BASS_ChannelIsActive", bass))
BASS_ChannelGetData = WINFUNCTYPE(DWORD, DWORD, c_void_p, DWORD)(
("BASS_ChannelGetData", bass_module)
("BASS_ChannelGetData", bass)
)
@ -141,52 +112,83 @@ class playonce:
BASS_Init(-1, 44100, 0, 0, 0)
# https://www.un4seen.com/
plugins = {".spx": "bass_spx.dll", ".aac": "bass_aac.dll", ".opus": "bassopus.dll"}
pluginshandle = {}
plugins = ["bass_spx.dll", "bass_aac.dll", "bassopus.dll"]
for _ in plugins:
BASS_PluginLoad(gobject.GetDllpath(_).encode("utf8"), 0)
def load_ext(ext=None):
if ext and plugins.get(ext) and not pluginshandle.get(ext):
pluginshandle[ext] = BASS_PluginLoad(
gobject.GetDllpath(plugins.get(ext)).encode("utf8"), 0
)
def ENCODEPROCEXF(ret: list, _, _1, buffer, size, _2, _3):
ret.append(cast(buffer, POINTER(c_char))[:size])
def bass_decode(bs, ext=None):
load_ext(ext)
def ENCODEPROCF(ret: list, _, _1, buffer, size, _2):
ret.append(cast(buffer, POINTER(c_char))[:size])
ENCODEPROC = WINFUNCTYPE(None, HENCODE, DWORD, c_void_p, DWORD, c_void_p)
ENCODEPROCEX = WINFUNCTYPE(None, HENCODE, DWORD, c_void_p, DWORD, QWORD, c_void_p)
encoders = {
"mp3": [
"bassenc_mp3.dll",
"BASS_Encode_MP3_Start",
"mp3",
ENCODEPROCEXF,
ENCODEPROCEX,
],
"opus": [
"bassenc_opus.dll",
"BASS_Encode_OPUS_Start",
"ogg",
ENCODEPROCF,
ENCODEPROC,
],
}
BASS_Encode_Start_T = WINFUNCTYPE(HENCODE, DWORD, c_wchar_p, DWORD, c_void_p, c_void_p)
def load_enc_func(ext):
_ = encoders.get(ext)
if not _:
return None
dll, fun = _[0], _[1]
if isinstance(dll, str):
dll = WinDLL(gobject.GetDllpath(dll))
_[0] = dll
if isinstance(fun, str):
fun = BASS_Encode_Start_T((fun, dll))
_[1] = fun
return _
def bass_code_cast(bs, fr="mp3"):
# fr没啥用仅用来给出编码失败时的用来占位的后缀以少写代码
to = globalconfig["audioformat"]
_ = load_enc_func(to)
if not _:
return bs, fr
_, start, ext, func, funct = _
stream = BASS_StreamCreateFile(True, bs, 0, len(bs), BASS_STREAM_DECODE)
if not stream:
return
info = BASS_CHANNELINFO()
if not BASS_ChannelGetInfo(stream, pointer(info)):
return
wf = WAVEFORMATEX()
wf.wFormatTag = 1
wf.nChannels = info.chans
wf.wBitsPerSample = 8 if info.flags & BASS_SAMPLE_8BITS else 16
wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample // 8
wf.nSamplesPerSec = info.freq
wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign
res = []
size = 0
buff = create_string_buffer(0x10000)
return bs, fr
ret = []
func = funct(functools.partial(func, ret))
if to == "mp3":
opts = "-b{}".format(globalconfig["mp3kbps"])
elif to == "opus":
opts = "--bitrate {}".format(globalconfig["opusbitrate"])
encoder = start(stream, opts, BASS_UNICODE, func, None)
if not encoder:
BASS_StreamFree(stream)
return bs, fr
buff = create_string_buffer(0x10000) # wav仅用于激活getdata
while BASS_ChannelIsActive(stream):
get = BASS_ChannelGetData(stream, buff, 0x10000)
res.append(buff[:get])
size += get
header = []
header.append(b"RIFF")
header.append(bytes(c_int(size + 44)))
header.append(b"WAVE")
header.append(b"fmt ")
header.append(bytes(c_int(sizeof(WAVEFORMATEX))))
header.append(bytes(wf))
header.append(b"data")
header.append(bytes(c_int(size)))
header.extend(res)
data = b"".join(header)
return winsharedutils.encodemp3(data, 64)
if not BASS_Encode_IsActive(stream):
break
_ = BASS_ChannelGetData(stream, buff, 0x10000)
BASS_Encode_Stop(stream)
BASS_StreamFree(stream)
print(len(bs), len(b"".join(ret)))
return b"".join(ret), ext
class series_audioplayer:
@ -209,11 +211,10 @@ class series_audioplayer:
except:
pass
def play(self, binary, volume=100, force=False, timestamp=None, ext=None):
def play(self, binary, volume=100, force=False, timestamp=None):
if timestamp and (timestamp != self.timestamp):
return
self.timestamp = timestamp
load_ext(ext)
try:
self.tasks = (binary, volume, force)
self.lock.release()

View File

@ -125,7 +125,7 @@ def getpidexe(pid):
def getcurrexe():
return os.environ.get("LUNA_EXE_NAME", "")
return getpidexe(os.getpid())
def test_injectable_1(pid):

View File

@ -21,6 +21,7 @@ import threading, winreg
import re, heapq, winsharedutils
from myutils.wrapper import tryprint, threader
from html.parser import HTMLParser
from myutils.audioplayer import bass_code_cast
def qimage2binary(qimage: QImage, fmt="BMP"):
@ -839,17 +840,11 @@ class loopbackrecorder:
wav = self.capture.stop()
if not wav:
return callback("")
mp3 = winsharedutils.encodemp3(wav)
if not mp3:
file = gobject.gettempdir(str(time.time()) + ".wav")
with open(file, "wb") as ff:
ff.write(wav)
callback(file)
else:
file = gobject.gettempdir(str(time.time()) + ".mp3")
with open(file, "wb") as ff:
ff.write(mp3)
callback(file)
new, ext = bass_code_cast(wav, "wav")
file = gobject.gettempdir(str(time.time()) + "." + ext)
with open(file, "wb") as ff:
ff.write(new)
callback(file)
def copytree(src, dst, copy_function=shutil.copy2):

View File

@ -1,6 +1,6 @@
try:
from PyQt5 import QtSvg
from PyQt5.QtWidgets import QFrame,QListView,QCheckBox,QAbstractItemView,QTextEdit,QTableView,QHeaderView,QColorDialog,QSpinBox,QDoubleSpinBox,QComboBox,QDialogButtonBox,QMainWindow,QMessageBox,QDialog,QGridLayout,QTextBrowser,QGraphicsDropShadowEffect,QWidget,QSizePolicy,QScrollArea,QApplication,QPushButton,QSystemTrayIcon,QPlainTextEdit,QAction,QMenu,QFileDialog,QKeySequenceEdit,QLabel,QSpacerItem,QWidgetItem,QLayout,QTextBrowser,QLineEdit,QFormLayout,QSizePolicy,QTabWidget,QTabBar,QSplitter,QListWidget,QListWidgetItem,QHBoxLayout,QVBoxLayout,QSizeGrip,QFontComboBox,QProgressBar,QRadioButton,QButtonGroup,QSlider,QToolTip,QGroupBox,QGraphicsOpacityEffect,QStackedWidget,QStyledItemDelegate,QStyleOptionViewItem,QFontDialog,QTreeView,QToolButton,QAbstractSpinBox
from PyQt5.QtWidgets import QFrame,QListView,QCheckBox,QAbstractItemView,QTextEdit,QTableView,QHeaderView,QColorDialog,QSpinBox,QDoubleSpinBox,QComboBox,QDialogButtonBox,QMainWindow,QMessageBox,QDialog,QGridLayout,QTextBrowser,QGraphicsDropShadowEffect,QWidget,QScrollArea,QApplication,QPushButton,QSystemTrayIcon,QPlainTextEdit,QAction,QMenu,QFileDialog,QKeySequenceEdit,QLabel,QSpacerItem,QWidgetItem,QLayout,QTextBrowser,QLineEdit,QFormLayout,QSizePolicy,QTabWidget,QTabBar,QSplitter,QListWidget,QListWidgetItem,QHBoxLayout,QVBoxLayout,QSizeGrip,QFontComboBox,QProgressBar,QRadioButton,QButtonGroup,QSlider,QToolTip,QGroupBox,QGraphicsOpacityEffect,QStackedWidget,QStyledItemDelegate,QStyleOptionViewItem,QFontDialog,QTreeView,QToolButton,QAbstractSpinBox
from PyQt5.QtGui import QIconEngine,QIntValidator,QStandardItem,QStandardItemModel,QImageWriter,QIcon,QTextCharFormat,QTextBlockFormat,QResizeEvent,QTextCursor,QFontMetricsF,QMouseEvent,QImage,QPainter,QRegion,QCloseEvent,QFontDatabase,QKeySequence,QPixmap,QCursor,QColor,QFont,QPen,QPainterPath,QBrush,QFontMetrics,QShowEvent,QWheelEvent,QPaintEvent,QTextLayout, QTextOption,QDragEnterEvent, QDropEvent,QTransform,QKeyEvent,QInputMethodEvent,QValidator
from PyQt5.QtCore import QObject,pyqtSignal,Qt,QSize,QByteArray,QBuffer,QPointF,QPoint,QRect,QEvent,QModelIndex,QTimer,QRectF,QVariantAnimation,QUrl,QPropertyAnimation,QLocale,QSignalBlocker,QMargins
isqt5 = True
@ -11,9 +11,9 @@ except:
#from traceback import print_exc
#print_exc()
from PyQt6 import QtSvg
from PyQt6.QtWidgets import QFrame,QListView,QCheckBox,QAbstractItemView,QTextEdit,QTableView,QHeaderView,QColorDialog,QSpinBox,QDoubleSpinBox,QComboBox,QDialogButtonBox,QMainWindow,QMessageBox,QDialog,QGridLayout,QTextBrowser,QGraphicsDropShadowEffect,QWidget,QSizePolicy,QScrollArea,QApplication,QPushButton,QSystemTrayIcon,QPlainTextEdit,QMenu,QFileDialog,QKeySequenceEdit,QLabel,QSpacerItem,QWidgetItem,QLayout,QTextBrowser,QLineEdit,QFormLayout,QSizePolicy,QTabWidget,QTabBar,QSplitter,QListWidget,QListWidgetItem,QHBoxLayout,QVBoxLayout,QSizeGrip,QFontComboBox,QProgressBar,QRadioButton,QButtonGroup,QSlider,QToolTip,QGroupBox,QGraphicsOpacityEffect,QStackedWidget,QTreeView,QToolButton,QAbstractSpinBox
from PyQt6.QtGui import QIconEngine,QIntValidator,QAction,QStandardItem,QStandardItemModel,QImageWriter,QIcon,QTextCharFormat,QTextBlockFormat,QResizeEvent,QTextCursor,QFontMetricsF,QMouseEvent,QImage,QPainter,QRegion,QCloseEvent,QFontDatabase,QKeySequence,QPixmap,QCursor,QColor,QFont,QPen,QPainterPath,QBrush,QFontMetrics,QShowEvent,QWheelEvent,QPaintEvent,QTextLayout, QTextOption,QKeyEvent,QInputMethodEvent,QValidator
from PyQt6.QtCore import QObject,pyqtSignal,Qt,QSize,QByteArray,QBuffer,QPointF,QPoint,QRect,QEvent,QModelIndex,QTimer,QRectF,QVariantAnimation,QUrl,QPropertyAnimation,QLocale,QMargins
from PyQt6.QtWidgets import QFrame,QListView,QCheckBox,QAbstractItemView,QTextEdit,QTableView,QHeaderView,QColorDialog,QSpinBox,QDoubleSpinBox,QComboBox,QDialogButtonBox,QMainWindow,QMessageBox,QDialog,QGridLayout,QTextBrowser,QGraphicsDropShadowEffect,QWidget,QScrollArea,QApplication,QPushButton,QSystemTrayIcon,QPlainTextEdit,QMenu,QFileDialog,QKeySequenceEdit,QLabel,QSpacerItem,QWidgetItem,QLayout,QTextBrowser,QLineEdit,QFormLayout,QSizePolicy,QTabWidget,QTabBar,QSplitter,QListWidget,QListWidgetItem,QHBoxLayout,QVBoxLayout,QSizeGrip,QFontComboBox,QProgressBar,QRadioButton,QButtonGroup,QSlider,QToolTip,QGroupBox,QGraphicsOpacityEffect,QStackedWidget,QTreeView,QToolButton,QAbstractSpinBox,QStyledItemDelegate,QStyleOptionViewItem,QFontDialog
from PyQt6.QtGui import QIconEngine,QIntValidator,QAction,QStandardItem,QStandardItemModel,QImageWriter,QIcon,QTextCharFormat,QTextBlockFormat,QResizeEvent,QTextCursor,QFontMetricsF,QMouseEvent,QImage,QPainter,QRegion,QCloseEvent,QFontDatabase,QKeySequence,QPixmap,QCursor,QColor,QFont,QPen,QPainterPath,QBrush,QFontMetrics,QShowEvent,QWheelEvent,QPaintEvent,QTextLayout, QTextOption,QKeyEvent,QInputMethodEvent,QValidator,QDragEnterEvent,QDropEvent,QTransform
from PyQt6.QtCore import QObject,pyqtSignal,Qt,QSize,QByteArray,QBuffer,QPointF,QPoint,QRect,QEvent,QModelIndex,QTimer,QRectF,QVariantAnimation,QUrl,QPropertyAnimation,QLocale,QMargins,QSignalBlocker
isqt5 = False
class LineHeightTypes:

View File

@ -1,4 +1,3 @@
from PyQt5.QtGui import QMouseEvent
from qtsymbols import *
from myutils.config import globalconfig, static_data
from rendertext.somefunctions import dataget
@ -459,7 +458,7 @@ class TextBrowser(QWidget, dataget):
layout = QTextLayout()
layout.setFont(font)
layout.setTextOption(QTextOption(Qt.AlignLeft))
layout.setTextOption(QTextOption(Qt.AlignmentFlag.AlignLeft))
layout.setText(linetext)
layout.beginLayout()
newtag = []

View File

@ -352,21 +352,6 @@ clipboard_callback_stop = utilsdll.clipboard_callback_stop
clipboard_callback_stop.argtypes = (HWND,)
clipboard_callback_type = CFUNCTYPE(None, c_wchar_p, c_bool)
_encodemp3 = utilsdll.encodemp3
_encodemp3.argtypes = c_void_p, c_size_t, c_void_p, c_int
def encodemp3(wav, bitr=320):
ret = []
def cb(ptr, size):
ret.append(cast(ptr, POINTER(c_char))[:size])
_encodemp3(wav, len(wav), CFUNCTYPE(None, c_void_p, c_size_t)(cb), bitr)
if len(ret):
return ret[0]
return None
GetMonitorDpiScaling = utilsdll.GetMonitorDpiScaling
GetMonitorDpiScaling.argtypes = (HWND,)

View File

@ -484,6 +484,9 @@
"tags": []
},
"imageformat": -1,
"mp3kbps": 64,
"opusbitrate": 10,
"audioformat": "mp3",
"ankiwindow": [
100,
100,
@ -1250,29 +1253,29 @@
},
"cishu": {
"mojidict": {
"use": false,
"use": true,
"name": "Moji辞书",
"useproxy": false
},
"youdao": {
"use": false,
"use": true,
"name": "有道词典",
"useproxy": false
},
"jisho": {
"use": false,
"use": true,
"name": "jisho"
},
"japandict": {
"use": false,
"use": true,
"name": "JapanDict"
},
"weblio": {
"use": false,
"use": true,
"name": "weblio"
},
"goo": {
"use": false,
"use": true,
"name": "goo"
},
"gemini": {

View File

@ -765,5 +765,6 @@
"全部": "كامل",
"年度总结": "ملخص سنوي",
"取词翻译": "اختيار الترجمة",
"取词查词": "كلمة البحث"
"取词查词": "كلمة البحث",
"音频编码": "ترميز الصوت"
}

View File

@ -765,5 +765,6 @@
"全部": "全部",
"年度总结": "年度總結",
"取词翻译": "取詞翻譯",
"取词查词": "取詞查詞"
"取词查词": "取詞查詞",
"音频编码": "音訊編碼"
}

View File

@ -765,5 +765,6 @@
"全部": "celý",
"年度总结": "Roční shrnutí",
"取词翻译": "Překlad výběru slova",
"取词查词": "Načíst a vyhledávat slova"
"取词查词": "Načíst a vyhledávat slova",
"音频编码": "Kódování zvuku"
}

View File

@ -765,5 +765,6 @@
"全部": "ganz",
"年度总结": "Jahreszusammenfassung",
"取词翻译": "Übersetzung der Wortauswahl",
"取词查词": "Wörter abrufen und suchen"
"取词查词": "Wörter abrufen und suchen",
"音频编码": "Audio-Codierung"
}

View File

@ -765,5 +765,6 @@
"全部": "all",
"年度总结": "Annual Summary",
"取词翻译": "Word selection translation",
"取词查词": "Retrieve and search for words"
"取词查词": "Retrieve and search for words",
"音频编码": "audio coding"
}

View File

@ -765,5 +765,6 @@
"全部": "Todo",
"年度总结": "Resumen Anual",
"取词翻译": "Traducción de palabras",
"取词查词": "Buscar palabras"
"取词查词": "Buscar palabras",
"音频编码": "Codificación de audio"
}

View File

@ -765,5 +765,6 @@
"全部": "Tous",
"年度总结": "Résumé annuel",
"取词翻译": "Traduction des mots",
"取词查词": "Recherche de mots"
"取词查词": "Recherche de mots",
"音频编码": "Codage audio"
}

View File

@ -765,5 +765,6 @@
"全部": "intero",
"年度总结": "Sintesi annuale",
"取词翻译": "Traduzione della selezione delle parole",
"取词查词": "Recupera e cerca parole"
"取词查词": "Recupera e cerca parole",
"音频编码": "Codificazione audio"
}

View File

@ -765,5 +765,6 @@
"全部": "すべて",
"年度总结": "年度まとめ",
"取词翻译": "単語の翻訳",
"取词查词": "単語を取って単語を調べる"
"取词查词": "単語を取って単語を調べる",
"音频编码": "オーディオコーディング"
}

View File

@ -765,5 +765,6 @@
"全部": "모두",
"年度总结": "연간 요약",
"取词翻译": "취사 번역",
"取词查词": "취사 조사"
"取词查词": "취사 조사",
"音频编码": "오디오 인코딩"
}

View File

@ -765,5 +765,6 @@
"全部": "geheel",
"年度总结": "Jaarlijkse samenvatting",
"取词翻译": "Woordselectie vertaling",
"取词查词": "Woorden ophalen en zoeken"
"取词查词": "Woorden ophalen en zoeken",
"音频编码": "Audiocodering"
}

View File

@ -765,5 +765,6 @@
"全部": "całość",
"年度总结": "Roczne podsumowanie",
"取词翻译": "Tłumaczenie wyboru słowa",
"取词查词": "Pobieranie i wyszukiwanie słów"
"取词查词": "Pobieranie i wyszukiwanie słów",
"音频编码": "Kodowanie dźwięku"
}

View File

@ -765,5 +765,6 @@
"全部": "inteiro",
"年度总结": "Resumo anual",
"取词翻译": "Tradução da selecção de palavras",
"取词查词": "Obter e procurar palavras"
"取词查词": "Obter e procurar palavras",
"音频编码": "Codificação de áudio"
}

View File

@ -765,5 +765,6 @@
"全部": "Все.",
"年度总结": "Ежегодное резюме",
"取词翻译": "Перевод",
"取词查词": "Поиск слов"
"取词查词": "Поиск слов",
"音频编码": "Звуковое кодирование"
}

View File

@ -765,5 +765,6 @@
"全部": "hela",
"年度总结": "Årlig sammanfattning",
"取词翻译": "Ordmarkeringsöversättning",
"取词查词": "Hämta och sök efter ord"
"取词查词": "Hämta och sök efter ord",
"音频编码": "Ljudkodning"
}

View File

@ -765,5 +765,6 @@
"全部": "ทั้งหมด",
"年度总结": "สรุปรายปี",
"取词翻译": "คำแปล",
"取词查词": "ค้นหาคำ"
"取词查词": "ค้นหาคำ",
"音频编码": "การเข้ารหัสเสียง"
}

View File

@ -765,5 +765,6 @@
"全部": "Tüm",
"年度总结": "Yıllık Toplantı",
"取词翻译": "Kelime seçimi çevirimi",
"取词查词": "Kelimeleri alın ve arayın"
"取词查词": "Kelimeleri alın ve arayın",
"音频编码": "Ses Kodlama"
}

View File

@ -765,5 +765,6 @@
"全部": "цілий",
"年度总结": "Річне резюме",
"取词翻译": "Переклад вибору слів",
"取词查词": "Отримати і шукати слова"
"取词查词": "Отримати і шукати слова",
"音频编码": "Аудіокодування"
}

View File

@ -765,5 +765,6 @@
"全部": "Tất cả",
"年度总结": "Tóm tắt hàng năm",
"取词翻译": "Dịch thuật lấy từ",
"取词查词": "Tìm kiếm từ"
"取词查词": "Tìm kiếm từ",
"音频编码": "Mã hóa âm thanh"
}

View File

@ -765,5 +765,6 @@
"全部": "",
"年度总结": "",
"取词翻译": "",
"取词查词": ""
"取词查词": "",
"音频编码": ""
}

View File

@ -1,4 +1,4 @@
PyQt5==5.15.10
PyQt5==5.15.11
PyQt5-Qt5==5.15.2
webviewpy==1.4.0
tinycss2