分类: IT小常识

  • 阿里云不再提供一年期限的免费域名SSL证书的应对策略

    毫无疑问,当然是用Let’s Encrypt

    那么就开始搞吧,事实上真的很简单。只是首先要搞清楚自己的部署环境,我用的是基于yeszao/dnmp改写的一个环境,大体上一样,可以直接看yeszao/dnmp。

    首先我把域名解析到我的服务器,如果是一个新的域名,则在得到ssl证书之前,给该域名的站点conf中只配置80端口,443端口等后续得到了数字证书以后再添加或解除注释,免得没有ssl的文件nginx无法启动。

    然后在该域名的站点的conf中添加

    location ^~ /.well-known/acme-challenge/ {
          default_type "text/plain";
          root /www/acme-challenge/;
        }

    注意,上面root后面对应的地址是基于dnmp的env配置的映射地址,也就是dnmp目录下的www中再新建一个acme-challenge,如果dnmp在/,则实际目录地址是/dnmp/www/acme-challenge,这个地址在下一步用的到。

    添加以上规则后,重启nginx。

    然后就可以使用命令创建证书了:

    sudo certbot certonly --webroot -w /dnmp/www/acme-challenge/ -d "*.isbn.ink" -d isbn.ink

    命令也可以明确–server参数

    sudo certbot certonly --webroot -w /dnmp/www/acme-challenge/ -d "*.isbn.ink" -d isbn.ink --server https://acme-v02.api.letsencrypt.org/directory

    certbot如何安装请看https://certbot.eff.org/instructions?ws=nginx&os=ubuntufocal&tab=standard

    按提示执行完就行了,甚至都自动创建好了更新任务,任务查看命令:systemctl list-timers

    NEXT                        LEFT               LAST                        PASSED       UNIT                           ACTIVATES                       
    Fri 2024-01-26 14:11:00 CST 19min left         n/a                         n/a          snap.certbot.renew.timer       snap.certbot.renew.service

    ssl证书文件默认会放在/etc/letsencrypt下面,那么结合dnmp的env配置项:

    NGINX_SSL_CERTIFICATE_DIR=/etc/letsencrypt

    如果有其他站点需要用到NGINX_SSL_CERTIFICATE_DIR的默认配置./services/nginx/ssl,那么还有个办法,即使用ln到/etc/letsencrypt/live:

    ln -s /etc/letsencrypt/live /dnmp/services/nginx/ssl

    那么,在站点的conf文件中,对443部分的设置如下:

    server {
        listen 443 ssl;
        server_name isbn.ink;
        charset utf8;
        #ssl on;
        ssl_certificate   /ssl/live/isbn.ink/fullchain.pem;
        ssl_certificate_key  /ssl/live/isbn.ink/privkey.pem;
        ssl_session_timeout 5m;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
    
        root   /www/localhost;
        index  index.php index.html index.htm;
    
        location / {
            if (!-e $request_filename) {
                rewrite (.*) /index.php;
            }
        }
    
        location ~ [^/]\.php(/|$) {
            fastcgi_pass   php80:9000;
            include        fastcgi-php.conf;
            include        fastcgi_params;
        }
    
    }

    可以看到我把live放到了conf中,而没有放在NGINX_SSL_CERTIFICATE_DIR设置中,因为live/isbn.ink/fullchain.pem是个软地址,实际文件是指向../../archive/isbn.ink/fullchain1.pem的,这里用了两层父目录,站点域名一层,live占了一层,所以ssl的映射目录只能设置为/etc/letsencrypt。

  • jenkins FATAL: Failed to install https://nodejs.org/dist/v16.14.2/node-v16.14.2-linux-x64.tar.gz

     > git rev-list --no-walk 7756bc509c8ed181c19b8b725a3b395e13ee7539 # timeout=10
    FATAL: Failed to install https://nodejs.org/dist/v16.14.2/node-v16.14.2-linux-x64.tar.gz to /var/jenkins_home/tools/jenkins.plugins.nodejs.tools.NodeJSInstallation/nodejs16.4.2
    java.net.SocketTimeoutException: connect timed out
    	at java.base/java.net.PlainSocketImpl.socketConnect(Native Method)
    	at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:412)
    	at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:255)
    	at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:237)
    	at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    	at java.base/java.net.Socket.connect(Socket.java:609)
    	at java.base/sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:305)
    	at java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:177)
    	at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:507)
    	at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:602)
    	at java.base/sun.net.www.protocol.https.HttpsClient.<init>(HttpsClient.java:266)
    	at java.base/sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:373)
    	at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(AbstractDelegateHttpsURLConnection.java:207)
    	at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1187)
    	at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1081)
    	at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:193)
    	at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:168)
    	at hudson.FilePath.installIfNecessaryFrom(FilePath.java:993)
    Caused: java.io.IOException: Failed to install https://nodejs.org/dist/v16.14.2/node-v16.14.2-linux-x64.tar.gz to /var/jenkins_home/tools/jenkins.plugins.nodejs.tools.NodeJSInstallation/nodejs16.4.2
    	at hudson.FilePath.installIfNecessaryFrom(FilePath.java:1067)
    	at hudson.FilePath.installIfNecessaryFrom(FilePath.java:980)
    	at jenkins.plugins.nodejs.tools.NodeJSInstaller.performInstallation(NodeJSInstaller.java:145)
    	at hudson.tools.InstallerTranslator.getToolHome(InstallerTranslator.java:70)
    	at hudson.tools.ToolLocationNodeProperty.getToolHome(ToolLocationNodeProperty.java:109)
    	at hudson.tools.ToolInstallation.translateFor(ToolInstallation.java:221)
    	at jenkins.plugins.nodejs.tools.NodeJSInstallation.forNode(NodeJSInstallation.java:95)
    	at jenkins.plugins.nodejs.NodeJSBuildWrapper.setUp(NodeJSBuildWrapper.java:158)
    	at jenkins.tasks.SimpleBuildWrapper.setUp(SimpleBuildWrapper.java:294)
    	at hudson.model.Build$BuildExecution.doRun(Build.java:158)
    	at hudson.model.AbstractBuild$AbstractBuildExecution.run(AbstractBuild.java:526)
    	at hudson.model.Run.execute(Run.java:1900)
    	at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:44)
    	at hudson.model.ResourceController.execute(ResourceController.java:101)
    	at hudson.model.Executor.run(Executor.java:442)
    Finished: FAILURE

    我的服务器环境是在docker中跑的jenkins,幸会我是把jenkins_home绑定到宿主机了。

      jenkins:
        image: jenkins/jenkins:${JENKINS_VERSION}
        container_name: jenkins
        volumes:
            - ${JENKINS_HOME_DIR}:/var/jenkins_home
            - ${JENKINS_CERTS_DIR}:/certs/client
            - ${SOURCE_DIR}:/www/:rw
            - ${TOMCAT_WEBAPPS_DIR}:/webapps/:rw
            - /var/run/docker.sock:/var/run/docker.sock
            - /usr/bin/docker:/usr/bin/docker
            - /usr/lib/x86_64-linux-gnu/libltdl.so.7:/usr/lib/x86_64-linux-gnu/libltdl.so.7
        ports:
            - "${JENKINS_HTTP_PORT}:8080"
            - 50000:50000
        privileged: true
        user: root
        restart: always
        environment:
            TZ: "$TZ"
            JAVA_OPTS: '-Djava.util.logging.config.file=/var/jenkins_home/log.properties'
            JENKINS_OPTS: '--prefix=/jenkins'
        networks:
          - default

    根据错误提示,我可以确定需要哪个文件,以及要放到什么地方。那我就从别的地方下载https://nodejs.org/dist/v16.14.2/node-v16.14.2-linux-x64.tar.gz,然后解压到/var/jenkins_home/tools/jenkins.plugins.nodejs.tools.NodeJSInstallation/nodejs16.4.2这个目录,实际上这个目录对应的是我宿主机的某个文件夹,这样也方便操作。

    没想到他们连nodejs都要封。

  • 完美解决wsl+docker的二级代理上网问题

    继续上一篇,基于wsl2的docker-desktop创建fast-whisper无法正常运行,错误日志如下:

    2023-07-06 17:10:29 Traceback (most recent call last):
    2023-07-06 17:10:29   File "/usr/local/lib/python3.8/dist-packages/gradio/routes.py", line 394, in run_predict
    2023-07-06 17:10:29     output = await app.get_blocks().process_api(
    2023-07-06 17:10:29   File "/usr/local/lib/python3.8/dist-packages/gradio/blocks.py", line 1075, in process_api
    2023-07-06 17:10:29     result = await self.call_function(
    2023-07-06 17:10:29   File "/usr/local/lib/python3.8/dist-packages/gradio/blocks.py", line 884, in call_function
    2023-07-06 17:10:29     prediction = await anyio.to_thread.run_sync(
    2023-07-06 17:10:29   File "/usr/local/lib/python3.8/dist-packages/anyio/to_thread.py", line 31, in run_sync
    2023-07-06 17:10:29     return await get_asynclib().run_sync_in_worker_thread(
    2023-07-06 17:10:29   File "/usr/local/lib/python3.8/dist-packages/anyio/_backends/_asyncio.py", line 937, in run_sync_in_worker_thread
    2023-07-06 17:10:29     return await future
    2023-07-06 17:10:29   File "/usr/local/lib/python3.8/dist-packages/anyio/_backends/_asyncio.py", line 867, in run
    2023-07-06 17:10:29     result = context.run(func, *args)
    2023-07-06 17:10:29   File "app.py", line 103, in transcribe_webui_simple_progress
    2023-07-06 17:10:29     return self.transcribe_webui(modelName, languageName, urlData, multipleFiles, microphoneData, task, vadOptions,
    2023-07-06 17:10:29   File "app.py", line 197, in transcribe_webui
    2023-07-06 17:10:29     result = self.transcribe_file(model, source.source_path, selectedLanguage, task, vadOptions, scaled_progress_listener, **decodeOptions)
    2023-07-06 17:10:29   File "app.py", line 280, in transcribe_file
    2023-07-06 17:10:29     process_gaps = self._create_silero_config(NonSpeechStrategy.CREATE_SEGMENT, vadOptions)
    2023-07-06 17:10:29   File "app.py", line 368, in _create_silero_config
    2023-07-06 17:10:29     self.vad_model = VadSileroTranscription()
    2023-07-06 17:10:29   File "/opt/whisper-webui/src/vad.py", line 437, in __init__
    2023-07-06 17:10:29     self._initialize_model()
    2023-07-06 17:10:29   File "/opt/whisper-webui/src/vad.py", line 445, in _initialize_model
    2023-07-06 17:10:29     self.model, self.get_speech_timestamps = self._create_model()
    2023-07-06 17:10:29   File "/opt/whisper-webui/src/vad.py", line 449, in _create_model
    2023-07-06 17:10:29     model, utils = torch.hub.load(repo_or_dir='snakers4/silero-vad', model='silero_vad')
    2023-07-06 17:10:29   File "/usr/local/lib/python3.8/dist-packages/torch/hub.py", line 537, in load
    2023-07-06 17:10:29     repo_or_dir = _get_cache_or_reload(repo_or_dir, force_reload, trust_repo, "load",
    2023-07-06 17:10:29   File "/usr/local/lib/python3.8/dist-packages/torch/hub.py", line 180, in _get_cache_or_reload
    2023-07-06 17:10:29     repo_owner, repo_name, ref = _parse_repo_info(github)
    2023-07-06 17:10:29   File "/usr/local/lib/python3.8/dist-packages/torch/hub.py", line 133, in _parse_repo_info
    2023-07-06 17:10:29     with urlopen(f"https://github.com/{repo_owner}/{repo_name}/tree/main/"):
    2023-07-06 17:10:29   File "/usr/lib/python3.8/urllib/request.py", line 222, in urlopen
    2023-07-06 17:10:29     return opener.open(url, data, timeout)
    2023-07-06 17:10:29   File "/usr/lib/python3.8/urllib/request.py", line 525, in open
    2023-07-06 17:10:29     response = self._open(req, data)
    2023-07-06 17:10:29   File "/usr/lib/python3.8/urllib/request.py", line 542, in _open
    2023-07-06 17:10:29     result = self._call_chain(self.handle_open, protocol, protocol +
    2023-07-06 17:10:29   File "/usr/lib/python3.8/urllib/request.py", line 502, in _call_chain
    2023-07-06 17:10:29     result = func(*args)
    2023-07-06 17:10:29   File "/usr/lib/python3.8/urllib/request.py", line 1397, in https_open
    2023-07-06 17:10:29     return self.do_open(http.client.HTTPSConnection, req,
    2023-07-06 17:10:29   File "/usr/lib/python3.8/urllib/request.py", line 1357, in do_open
    2023-07-06 17:10:29     raise URLError(err)
    2023-07-06 17:10:29 urllib.error.URLError: <urlopen error [Errno 111] Connection refused>
    2023-07-06 17:10:29 Traceback (most recent call last):
    2023-07-06 17:10:29   File "/usr/lib/python3.8/urllib/request.py", line 1354, in do_open
    2023-07-06 17:10:29     h.request(req.get_method(), req.selector, req.data, headers,
    2023-07-06 17:10:29   File "/usr/lib/python3.8/http/client.py", line 1256, in request
    2023-07-06 17:10:29     self._send_request(method, url, body, headers, encode_chunked)
    2023-07-06 17:10:29   File "/usr/lib/python3.8/http/client.py", line 1302, in _send_request
    2023-07-06 17:10:29     self.endheaders(body, encode_chunked=encode_chunked)
    2023-07-06 17:10:29   File "/usr/lib/python3.8/http/client.py", line 1251, in endheaders
    2023-07-06 17:10:29     self._send_output(message_body, encode_chunked=encode_chunked)
    2023-07-06 17:10:29   File "/usr/lib/python3.8/http/client.py", line 1011, in _send_output
    2023-07-06 17:10:29     self.send(msg)
    2023-07-06 17:10:29   File "/usr/lib/python3.8/http/client.py", line 951, in send
    2023-07-06 17:10:29     self.connect()
    2023-07-06 17:10:29   File "/usr/lib/python3.8/http/client.py", line 1418, in connect
    2023-07-06 17:10:29     super().connect()
    2023-07-06 17:10:29   File "/usr/lib/python3.8/http/client.py", line 922, in connect
    2023-07-06 17:10:29     self.sock = self._create_connection(
    2023-07-06 17:10:29   File "/usr/lib/python3.8/socket.py", line 808, in create_connection
    2023-07-06 17:10:29     raise err
    2023-07-06 17:10:29   File "/usr/lib/python3.8/socket.py", line 796, in create_connection
    2023-07-06 17:10:29     sock.connect(sa)
    2023-07-06 17:10:29 ConnectionRefusedError: [Errno 111] Connection refused

    很明显是github无法访问,我首先想到的是docker无法走系统的代理,wsl也不行,通过在docker和wsl中运行curl https://www.google.com可以测试。(fast-whisper镜像不像whiper那样把东西都放进去了,每次运行时还要加载东西,造成这个问题。也就是说whisper官方镜像可以全脱网运行,fast-whisper则不行。)

    那么怎么让没有开通局域网共享功能的梯子来分享网络呢?通过一阵子检索,终于发现了二级代理和链式代理这个概念,最终确定用gost试一试。

    1 在windows上找到当前的代理

    打开网络连接的“网络和Internet设置”,打开设置中的“代理”。如果你安装了某个代理程序,则这里应该能够看到设置的端口。比如地址:http://localhost,端口:15236。

    注意,这里的localhost很重要,因为收费的代理不希望你局域网共享,所以限制只能通过localhost访问。如果没有这个限制,那就简单了,直接把代理设置为主机的IP和端口就行了。

    2 安装gost作为二级代理

    由于我的梯子是在win10上的,所以需要让gost也在win上运行,所以安装了win版的go,然后安装/build这个gost,目录下就有gost.ext了,运行:

    .\gost -L=:15230 -F=localhost:15236

    二级代理就启动了,最直接的是在另外一台电脑上设置这个IP加端口15230试试,发现代理成功。试了一下,必须是localhost,不能是127.0.0.1。

    为了方便,我把这个设置了自动启动,在gost.exe所在目录增加一个名为gost.ps1的文件,内容如下:

    .\gost.exe -L=:16236 -F=localhost:15236

    同样在这个目录再增加一个gost.bat,内容如下:

    Powershell.exe -executionpolicy remotesigned -File ./gost.ps1

    然后在gost.bat上右键创建一个快捷方式,把这个快捷方式放到开机启动目录C:\Users\PS\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup

    可以通过徽标键+R打开shell:startup

    3 在wsl中配置代理的启用

    然后可以借鉴WSL2配置代理 – Leaos – 博客园 (cnblogs.com)来设置wsl2中的代理。

    还有个问题,DNS也要先加上8.8.8.8,不然域名解析被污染了,依然无法访问github。

    一个更方便的方法,在wsl的用户主目录增加一个文件proxy.sh,内容如下:

    #!/bin/sh
    # hostip=$(cat /etc/resolv.conf | grep nameserver | awk '{ print $2 }')
    hostip='192.168.0.200'
    wslip=$(hostname -I | awk '{print $1}')
    port=16236
     
    PROXY_HTTP="http://${hostip}:${port}"
    PROXY_SOCKS5="socks5://${hostip}:${port}"
     
    set_proxy(){
      export http_proxy="${PROXY_HTTP}"
      export HTTP_PROXY="${PROXY_HTTP}"
     
      export https_proxy="${PROXY_HTTP}"
      export HTTPS_proxy="${PROXY_HTTP}"
     
      export ALL_PROXY="${PROXY_SOCKS5}"
      export all_proxy="${PROXY_SOCKS5}"
     
      git config --global http.https://github.com.proxy ${PROXY_HTTP}
      git config --global https.https://github.com.proxy ${PROXY_HTTP}
     
      echo "Proxy has been opened."
    
      test_setting
    }
     
    unset_proxy(){
      unset http_proxy
      unset HTTP_PROXY
      unset https_proxy
      unset HTTPS_PROXY
      unset ALL_PROXY
      unset all_proxy
      git config --global --unset http.https://github.com.proxy
      git config --global --unset https.https://github.com.proxy
     
      echo "Proxy has been closed."
    }
     
    test_setting(){
      echo "Host IP:" ${hostip}
      echo "WSL IP:" ${wslip}
      echo "Current https_proxy:" $https_proxy
      echo "Current all_proxy:" $all_proxy
      echo "Try to connect to Google..."
      resp=$(curl -I -s --connect-timeout 5 -m 5 -w "%{http_code}" -o /dev/null www.google.com)
      if [ ${resp} = 200 ]; then
        echo "Proxy setup succeeded!"
      else
        echo "Proxy setup failed!"
      fi
    }
     
    if [ "$1" = "set" ]
    then
      set_proxy
     
    elif [ "$1" = "unset" ]
    then
      unset_proxy
     
    elif [ "$1" = "test" ]
    then
      test_setting
    else
      echo "Unsupported arguments."
    fi

    注意,其中的hostip我没有用其他文档给的方法,因为不确定一定对,我改成了本机的固定IP。

    然后在.bashrc文件结尾增加

    alias proxy="source ~/proxy.sh set"
    alias unproxy="source ~/proxy.sh unset"

    这样,在source ~/.bashrc之后,就可以直接在shell中输入proxy来启用,或者输入unproxy来停用了。

    再进一步,想要在打开shell时自动配置,可以新建一个autoproxy.sh

    #!/bin/sh
    # hostip=$(cat /etc/resolv.conf | grep nameserver | awk '{ print $2 }')
    hostip='192.168.0.200'
    wslip=$(hostname -I | awk '{print $1}')
    port=16236
     
    PROXY_HTTP="http://${hostip}:${port}"
    PROXY_SOCKS5="socks5://${hostip}:${port}"
    
    
    export http_proxy="${PROXY_HTTP}"
    export HTTP_PROXY="${PROXY_HTTP}"
    
    export https_proxy="${PROXY_HTTP}"
    export HTTPS_proxy="${PROXY_HTTP}"
    
    export ALL_PROXY="${PROXY_SOCKS5}"
    export all_proxy="${PROXY_SOCKS5}"
    
    git config --global http.https://github.com.proxy ${PROXY_HTTP}
    git config --global https.https://github.com.proxy ${PROXY_HTTP}
    
    echo "Proxy has been opened."

    然后在.bashrc中增加

    
    if [ -f "/home/yourusername/autoproxy.sh" ]; then
        . "/home/yourusername/autoproxy.sh"
    fi

  • 本地化搭建fast-whisper+ChatGLM2-6B出来会谈记录

    ffmpeg -i  20230703am.mp3 -f segment -segment_time 1755 -write_xing 0 -c copy  20230703am/out%03d.mp3

    首先用以上命令将长音频切分为每30分钟一段,因为fast-whisper有这样的限制,实际切分的时候不要设置为1800秒,因为会多一点点,无法通过,可以设置到1799秒。

    fast-whisper用官方的docker方案直接跑,试了一下medium模型和largv2都还行,默认就用medium了。

    转录出来的文本用ChatGLM2-6B来处理,比如用这样的提示词:“请整理以下录音识别的文字,去掉其中的语气词等多余的部分,将语句调整通顺,修复可能因为语音识别产生的错误。”

    ChatGLM2-6B直接按官方库的方法部署即可,通过webui访问。

    如果希望自己对照查看文本,可以用VLC打开audio的visualizations效果即可显示字幕,也可以用subtitle编辑。

    用VLC播放mp3的时候,默认看不到字幕,需要手动打开音频可视化。

    图形默认是正方的,看着有点别扭,那么再调整一下视频比例。

    更详细的情况欢迎联系我交流。

  • 自动压缩当前文件夹及所有子文件夹的图片代码

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
    
    '''
    Author: be_loving@163.com 
    Date: 2023-06-26 20:13:21
    LastEditors: be_loving@163.com 
    LastEditTime: 2023-06-26 21:28:50
    Description: 
    
    Copyright (c) 2023 Jiulu LTD, All Rights Reserved. 
    '''
    # pip install Pillow
    import os
    from PIL import Image
    Image.MAX_IMAGE_PIXELS = None
    
    for root, dirs, files in os.walk(".", topdown=False):
        for name in files:
    
            if name.endswith(".jpg") or name.endswith(".jpeg") or name.endswith(".png") or name.endswith(".tif"):
                img_path = os.path.join(root, name)
                print(img_path)
                if img_path.count('_144') > 1:
                    os.remove(img_path)
                elif img_path.count('_144') == 0:
                    new_img_path = os.path.join(
                        root, f"{name}_144.jpg")
                    print(new_img_path)
                    if not os.path.isfile(new_img_path):
                        try:
                            with Image.open(img_path) as img:
                                img.save(new_img_path, dpi=(144, 144))
                        except(OSError, NameError):
                            print('OSError,Path:', img_path)
    
        # for name in dirs:
        #     print(os.path.join(root, name))
    

    具体压缩参数及文件命名方式请自己参照pillow的文档修改。

    脚本运行时可执行

    python ./resize.py

    但需要当前的python环境支持pillow,可以直接安装,也可以用conda来定制环境,比如在conda create命令下安装pillow之后用conda activate pillow激活该环境。

  • 萤石云录像机CS-N1-208硬盘报警消除

    萤石云录像机CS-N1-208突然间开始连续发出滴滴滴三声蜂鸣,打客服电话问,说可以把报警声关掉。

    但检查后发现不只是有报警音的问题,关键是硬盘不转,以为硬盘坏了,换了一个硬盘,依然不转,进入系统,识别不到硬盘。以为主板坏了,打算换一个主板,结果在淘宝上了咨询了一下老板,直接回复是电源的问题,原来的使用的电源是0.7A(12V),远远不够,建议升到2A以上。

    于是用一个笔记本电脑的电源(19V)加上一个可调降压模块(12V),开机成功,硬盘也恢复了。

    我不理解的是之前它是怎么正常工作的。

    顺带备注一下:这个录像机恢复初始密码后,密码是盒子标签上的验证码,区分大小写。网上查到的文档都是说12345,不对。

  • 在ox11以上版本的mac上安装DocuPrint C1110B

    DocuPrint C1110B大概是针对中国大陆市场投放的一个打印机型号,目前官方网站已经停止了对这个型号的售后支持,包括驱动下载,即使各种搜索,也只能在非官方网站上找到兼容到ox10的驱动安装包。

    一直找到很晚,因为眼睛看花了,误把DocuPrint C1100看成了DocuPrint C1110,所以将错就错在下面这个网址下载了DocuPrint C1100的驱动。

    https://www.fujifilm.com/fb/download/eng/docuprint

    打开上面的网址,选择相应的型号,点击该型号链接,打开新的页面,切换到mac的tab上,选择想要的系统版本项,打开的是一个日文的页面,没关系,用一下翻译插件,点击同意并下载,在mac上打开这个驱动并安装。

    在usb上连接上打印机,并开机,添加打印机,这个时候如果还是没有显示打印机型号,显示为普通的PLC打印机,则选择其他,然后定位到系统盘的“资源库-Printers-PPDS-Contents-Resources-FX DocuPrint C1100”即可添加成功。

    第二天再仔细看才发现具体型号是DocuPrint C1100,到官网查看了一下产品照片,其实和DocuPrint C1110一样,那么用的芯片实际上也是一样的了。在武汉的时候修打印机的师傅告诉我,打印机虽然那么多型号,但实际上芯片规格并不多,很多很神奇的联想方正打印机,直接到日本打印机官网去下载某型号的驱动就可以用,因为都是贴牌或套壳的。

  • vite项目引入vue3-carousel-3d时添加declare module ‘vue3-carousel-3d’

    vite/vue3中引入vue3-carousel-3d组件时会报错,vue3-carousel-3d是基于vue-carousel-3d改造的,但没有提供types文件。为了解决这个问题,需要在项目中自行添加一个文件,比如命名为vendor.d.ts(可以放在types文件夹,并在tsconfig.json的types数组中添加./types/vendor.d.ts),内容如下:

    declare module 'vue3-carousel-3d';

    这样,就能正常使用了。

  • Handshaker通过usb连接mac时需要选择PTP模式

    当开启了开发者调试以后,连接模式默认为MTP,mac上打开handshaker但无法连接,切换到PTP模式时,显示已连接,但程序会无响应,也许文件太多了。

  • vue 引进quill-image-resize-module 报错TypeError: Cannot read property ‘imports’ of undefined

    首先说解决方案,不止一个,这里发我觉得比较好的一种。

    在webpack中添加ProvidePlugin,有两种情况。

    webpack.base.conf.js

    如果是vuecli2,则会有./build/webpack.base.conf.js这样的文件,添加方式:

    如果没有引入webpack则需要:

    const webpack = require('webpack')

    找到config下的plugins,添加如下代码:

    plugins: [
       new webpack.ProvidePlugin({
           'window.Quill': 'quill/dist/quill.js',
           'Quill': 'quill/dist/quill.js'
     })
    ]

    vue.config.js

    对于vuecli3,则直接在vue.config.js中找到chainWebpack,添加

    chainWebpack: config => {
        // quill-image-resize-module插件报错imports
        config.plugin('provide').use(webpack.ProvidePlugin, [{
          'window.Quill': 'quill/dist/quill.js',
          Quill: 'quill/dist/quill.js'
        }])
    }

    回过来说一下quill的两种引入方式,一种是直接使用quill,一种是使用vue-quill-editor。

    quill

    <template>
      <div ref="editor"></div>
    </template>
    import 'quill/dist/quill.core.css'
    import 'quill/dist/quill.snow.css'
    import 'quill/dist/quill.bubble.css'
    import Quill from 'quill'
    import { ImageDrop } from 'quill-image-drop-module'
    import ImageResize from 'quill-image-resize-module'
    Quill.register('modules/imageDrop', ImageDrop)
    Quill.register('modules/imageResize', ImageResize)
        data() {
        return {
          quill: undefined,
          currentValue: '',
          options: {
            theme: 'snow',
            bounds: document.body,
            debug: 'warn',
            modules: {
              toolbar: [
                ['bold', 'italic', 'underline', 'strike'],
                ['blockquote', 'code-block'],
                [{ list: 'ordered' }, { list: 'bullet' }],
                // [{ 'script': 'sub' }, { 'script': 'super' }],
                [{ indent: '-1' }, { indent: '+1' }],
                // [{ 'direction': 'rtl' }],
                // [{ size: ['small', false, 'large', 'huge'] }],
                [{ header: [1, 2, 3, 4, 5, 6, false] }],
                [{ color: [] }, { background: [] }],
                // [{ 'font': [] }],
                [{ align: [] }],
                ['clean'],
                ['link', 'image']
              ],
              imageDrop: true,
              imageResize: {}  //注意这里的首字母小写,组件官方文档是大写开头,会报错quill Cannot import ImageResize. Are you sure it was registered?
            },
            placeholder: '书写你的内容',
            readOnly: false
          }
        }
      },
      mounted() {
        this.init()
      },
      methods: {
        init() {
          const editor = this.$refs.editor
          // 初始化编辑器
          this.quill = new window.Quill(editor, this.options)
          // 默认值
          this.quill.pasteHTML(this.currentValue)
          // 绑定事件
          this.quill.on('text-change', (delta, oldDelta, source) => {
            const html = this.$refs.editor.children[0].innerHTML
            const text = this.quill.getText()
            const quillSelf = this.quill
            // 更新内部的值
            this.currentValue = html
            // 发出事件 v-model
            this.$emit('input', html)
            // 发出事件
            this.$emit('change', { html, text, quillSelf })
          })
          // 将一些 quill 自带的事件传递出去
          this.quill.on('text-change', (delta, oldDelta, source) => {
            this.$emit('text-change', delta, oldDelta, source)
          })
          this.quill.on('selection-change', (range, oldRange, source) => {
            this.$emit('selection-change', range, oldRange, source)
          })
          this.quill.on('editor-change', (eventName, ...args) => {
            this.$emit('editor-change', eventName, ...args)
          })
        }
      }

    vue-quill-editor

    <template>
      <div class="hello">
         <quill-editor v-model="content"
                          :options="editorOption"
                          @blur="onEditorBlur($event)"
                          @focus="onEditorFocus($event)"
                          @ready="onEditorReady($event)">
            </quill-editor>
        </div>
    </template>
    
    <script>
    import 'quill/dist/quill.core.css'
    import 'quill/dist/quill.snow.css'
    import 'quill/dist/quill.bubble.css'
    import Quill from 'quill'
    import { quillEditor } from 'vue-quill-editor'
    import { ImageDrop } from 'quill-image-drop-module'
    import ImageResize from 'quill-image-resize-module'
    Quill.register('modules/imageDrop', ImageDrop)
    Quill.register('modules/imageResize', ImageResize)
    
    //自定义字体类型
    var fonts = [
      "SimSun",
      "SimHei",
      "Microsoft-YaHei",
      "KaiTi",
      "FangSong",
      "Arial",
      "Times-New-Roman",
      "sans-serif"
    ];
    var Font = Quill.import("formats/font");
    Font.whitelist = fonts; //将字体加入到白名单
    Quill.register(Font, true);
    
    export default {
      name: 'HelloWorld',
      components: {
        quillEditor
      },
      data () {
        return {
          contentCode:'',
          content: `<p><img src="http://t8.baidu.com/it/u=1484500186,1503043093&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1581398243&t=ccf50d7b4dd50dac437d46e368b66b20" width="500"></p>
             `,
         editorOption: {
            modules: {
              toolbar: [
                ['bold', 'italic', 'underline', 'strike', 'image'],
                ['formula', 'clean'],
                ['blockquote', 'code-block'],
                [{'list': 'ordered'}, {'list': 'bullet'}],
                [{'script': 'sub'}, {'script': 'super'}],
                [{'size': ['small', false, 'large', 'huge']}],
                [{ 'font': fonts }],
                [{'header': [1, 2, 3, 4, 5, 6, false]}],
                [{ 'color': [] }, { 'background': [] }],
                [{ 'align': [] }],
                [{'direction': 'rtl'}]
              ],
              history: {
                  delay: 1000,
                  maxStack: 50,
                  userOnly: false
                },
                imageDrop: true,
                imageResize: {
                  displayStyles: {
                    backgroundColor: 'black',
                    border: 'none',
                    color: 'white'
                  },
                  modules: [ 'Resize', 'DisplaySize', 'Toolbar' ]
                }
            },
            placeholder: '输入内容........'
          }
        }
      },
      methods: {
        //vue-quill-editor
        onEditorBlur(quill) {
          // console.log("editor blur!", quill, this.content);
          //this.$emit("editorBlur", this.content);
          this.contentCode=this.content
        },
        onEditorFocus(quill) {
          this.contentCode=this.content
        },
        onEditorReady(quill) {
          // console.log("editor ready!", quill);
        },
        onEditorChange({ quill, html, text }) {
          // console.log("editor change!", quill, html, text);
          this.content = html;
        },
        onEditorChange(quill) {
          // console.log("编辑内容改变!", quill, this.content);
          //this.$emit("editorBlur", this.content);
        },
      },
      mounted() {
        //  console.log("this is current quill instance object", this.editor);
      }
    }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped lang='scss'>
    </style>