Auto Play Audio Sequentially

使用GM_xmlhttpRequest访问指定URL获取音频并自动播放,确保按顺序播放

当前为 2024-04-15 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Auto Play Audio Sequentially
// @version      1
// @namespace    http://tampermonkey.net/
// @description  使用GM_xmlhttpRequest访问指定URL获取音频并自动播放,确保按顺序播放
// @author       Your Name
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @connet        192.168.10.2
// ==/UserScript==
(function() {
    'use strict';
    let uRL="http://192.168.10.2:9880";
    // 初始 URL
  //  const initialUrl = 'http://192.168.10.2:9880?text=你好,我是七七,是个小僵尸&text_lang=中文&ref_audio_path=./qiqi3.mp3&prompt_text=拉手…拉手!要、要被吹跑了!&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/qiqi_e8_s136.pth&gweight=GPT_weights/qiqi-e15.ckpt';
    // http://192.168.10.4:9880?text=你好,我是七七,是个小僵尸&text_lang=中文&ref_audio_path=./yunjin.mp3&prompt_text=拉手…拉手!要、要被吹跑了!&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/qiqi_e8_s136.pth&gweight=GPT_weights/qiqi-e15.ckpt
    //http://192.168.10.4:9880?text=你好,我是七七,是个小僵尸&text_lang=中文&ref_audio_path=./yunjin.mp3&prompt_text=不勒头,不画脸,今天乐得清闲。&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/yunjin_e8_s208.pth&gweight=GPT_weights/yunjin-e15.ckpt
    // 当前 URL
  //  let currentUrl = initialUrl;

    // 存储待播放的音频URL队列
    const audioQueue = [];
    // 存储待播放的音频队列
    const audios = [];
    // 是否有音频正在播放
    let isPlaying = false;
    //播放的序号
    let playnum = 0;
    //是否停止接收
    let iscont = true;
    //是否停止播放;
    let isstop = false;
    // 存储已经绑定过按钮的 div 元素
    const boundDivs = new Set();
    //用于存放当前播放的div
    let isPlayingDiv = "";
    //即将播放的div
    let nextPlayingDiv = "";

    const statements = {
  元气少女音: {
    生气: "?text=你好&text_lang=中文&ref_audio_path=yuyin/元气少女音/香菱/生气/37.讨厌的食物…_我是不会对食物有什么偏见的!只有不合适的做法,没有不合格的食物!.mp3&prompt_text=我是不会对食物有什么偏见的!只有不合适的做法,没有不合格的食物!&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/xiangling-e15.pth&gweight=GPT_weights/xiangling_e8_s192.ckpt",
    高兴: "?text=你好&text_lang=中文&ref_audio_path=yuyin/元气少女音/香菱/高兴/41.生日…_啊,找到你了!快来,我给你做了一整桌的菜喔!嘿嘿,不用急着夸我,先等你尝过味道再慢慢说吧!.mp3&prompt_text=啊,找到你了!快来,我给你做了一整桌的菜喔!嘿嘿,不用急着夸我,先等你尝过味道再慢慢说吧!&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/xiangling-e15.pth&gweight=GPT_weights/xiangling_e8_s192.ckpt",
    呻吟: "?text=你好&text_lang=中文&ref_audio_path=yuyin/元气少女音/香菱/呻吟/63.倒下·其二_突然…好渴啊….mp3&prompt_text=突然…好渴啊…&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/xiangling-e15.pth&gweight=GPT_weights/xiangling_e8_s192.ckpt",
    伤心:"?text=你好&text_lang=中文&ref_audio_path=yuyin/元气少女音/香菱/伤心/35.香菱的烦恼…_呜,史莱姆滑蘑菇一份也没卖出去。就因为卖相不好吗?口感明明很棒的呀….mp3&prompt_text=呜,史莱姆滑蘑菇一份也没卖出去。就因为卖相不好吗?口感明明很棒的呀…&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/xiangling-e15.pth&gweight=GPT_weights/xiangling_e8_s192.ckpt",
    闲聊:"?text=你好&text_lang=中文&ref_audio_path=yuyin/元气少女音/香菱/闲聊/3.闲聊·觅食_嗯,闲着也是闲着,还不如一起找找食材去!.mp3&prompt_text=嗯,闲着也是闲着,还不如一起找找食材去!&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/xiangling-e15.pth&gweight=GPT_weights/xiangling_e8_s192.ckpt"
    // 其他语气...
  },
  大叔音: {
    闲聊:"?text=你好&text_lang=中文&ref_audio_path=yuyin/大叔音/钟离/闲聊/1.闲聊·旅程_旅程总有一天会迎来终点,不必匆忙。.mp3&prompt_text=旅程总有一天会迎来终点,不必匆忙。&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/zhongli_e16_s432.pth&gweight=GPT_weights/zhongli-e30.ckpt",
    生气: "?text=你好&text_lang=中文&ref_audio_path=yuyin/大叔音/钟离/生气/41.收到赠礼·其二_烹饪是一件饶有趣味的事,想来对你而言也是如此。.mp3&prompt_text=烹饪是一件饶有趣味的事,想来对你而言也是如此。&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/zhongli_e16_s432.pth&gweight=GPT_weights/zhongli-e30.ckpt",
    高兴: "?text=你好&text_lang=中文&ref_audio_path=yuyin/大叔音/钟离/高兴/42.收到赠礼·其三_既有闲暇,不如找一处庭院,和你说一段故事吧。.mp3&prompt_text=既有闲暇,不如找一处庭院,和你说一段故事吧。&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/zhongli_e16_s432.pth&gweight=GPT_weights/zhongli-e30.ckpt",
    呻吟:"?text=你好&text_lang=中文&ref_audio_path=yuyin/大叔音/钟离/呻吟/67.倒下·其三_磐石…也会归于尘土….mp3&prompt_text=磐石…也会归于尘土…&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/zhongli_e16_s432.pth&gweight=GPT_weights/zhongli-e30.ckpt",
    伤心:"?text=你好&text_lang=中文&ref_audio_path=yuyin/大叔音/钟离/伤心/4.闲聊·怀旧_欲买桂花同载酒…只可惜故人,何日再见呢?.mp3&prompt_text=欲买桂花同载酒…只可惜故人,何日再见呢?&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/zhongli_e16_s432.pth&gweight=GPT_weights/zhongli-e30.ckpt",
  },
   萝莉音: {
    闲聊:"?text=你好&text_lang=中文&ref_audio_path=yuyin/萝莉音/可莉/闲聊/1.闲聊·收获_可莉今天又勇敢地抓到了花纹奇怪的蜥蜴!从没见过这种图案,你要看看吗?.mp3&prompt_text=可莉今天又勇敢地抓到了花纹奇怪的蜥蜴!从没见过这种图案,你要看看吗?&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/keli_e8_s248.pth&gweight=GPT_weights/keli-e15.ckpt",
    生气: "?text=你好&text_lang=中文&ref_audio_path=yuyin/萝莉音/可莉/生气/44.突破的感受·承_又进步了一点!不过琴团长总是说:「进步是分内之事。」.mp3&prompt_text=又进步了一点!不过琴团长总是说:「进步是分内之事。」&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/keli_e8_s248.pth&gweight=GPT_weights/keli-e15.ckpt",
    高兴: "?text=你好&text_lang=中文&ref_audio_path=yuyin/萝莉音/可莉/高兴/42.生日…_生日快乐!过生日很开心,你比可莉大,那过生日的次数一定比可莉多吧?可莉很羡慕!.mp3&prompt_text=生日快乐!过生日很开心,你比可莉大,那过生日的次数一定比可莉多吧?可莉很羡慕!&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/keli_e8_s248.pth&gweight=GPT_weights/keli-e15.ckpt",
    呻吟:"?text=你好&text_lang=中文&ref_audio_path=yuyin/萝莉音/可莉/呻吟/11.关于可莉自己·肇事_呜…这次居然炸歪了大风车的叶片…真对不起!.mp3&prompt_text=呜…这次居然炸歪了大风车的叶片…真对不起!&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/keli_e8_s248.pth&gweight=GPT_weights/keli-e15.ckpt",
    伤心:"?text=你好&text_lang=中文&ref_audio_path=yuyin/萝莉音/可莉/伤心/38.讨厌的食物…_讨厌蒙德蟹——什么蟹都讨厌——可莉要出去玩——不要坐在餐桌前慢慢剥壳——.mp3&prompt_text=讨厌蒙德蟹——什么蟹都讨厌——可莉要出去玩——不要坐在餐桌前慢慢剥壳——&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/keli_e8_s248.pth&gweight=GPT_weights/keli-e15.ckpt",
  },
   正太音: {
    闲聊:"?text=你好&text_lang=中文&ref_audio_path=yuyin/正太音/班尼特/闲聊/3.闲聊·调侃_俗话说「等在原地是不会有好事发生的」,当然以我的运气来说,往哪里走都一样啦….mp3&prompt_text=俗话说「等在原地是不会有好事发生的」,当然以我的运气来说,往哪里走都一样啦…&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/bannite_e8_s200.pth&gweight=GPT_weights/bannite-e15.ckpt",
    生气: "?text=你好&text_lang=中文&ref_audio_path=yuyin/正太音/班尼特/生气/61.生命值低·其三_绝地反击?这个我熟,让我来吧。.mp3&prompt_text=绝地反击?这个我熟,让我来吧。&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/bannite_e8_s200.pth&gweight=GPT_weights/bannite-e15.ckpt",
    高兴: "?text=你好&text_lang=中文&ref_audio_path=yuyin/正太音/班尼特/高兴/37.收到赠礼·其一_太幸福了!哈哈…哎呀!会不会把这阵子的好运全部用完了….mp3&prompt_text=太幸福了!哈哈…哎呀!会不会把这阵子的好运全部用完了…&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/bannite_e8_s200.pth&gweight=GPT_weights/bannite-e15.ckpt",
    呻吟:"?text=你好&text_lang=中文&ref_audio_path=yuyin/正太音/班尼特/呻吟/66.倒下·其三_没能突破…厄运的封锁….mp3&prompt_text=没能突破…厄运的封锁…&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/bannite_e8_s200.pth&gweight=GPT_weights/bannite-e15.ckpt",
    伤心:"?text=你好&text_lang=中文&ref_audio_path=yuyin/正太音/班尼特/伤心/17.关于「神之眼」…_没想到我这么倒霉的人也可以获得神的认可,既然神都没有放弃我,我又怎么能放弃我自己呢?.mp3&prompt_text=没想到我这么倒霉的人也可以获得神的认可,既然神都没有放弃我,我又怎么能放弃我自己呢?&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/bannite_e8_s200.pth&gweight=GPT_weights/bannite-e15.ckpt",
  },
   御姐音: {
    闲聊:"?text=你好&text_lang=中文&ref_audio_path=yuyin/御姐音/八重神子/闲聊/1.闲聊·创作体裁_最近八重堂穿越异世的小说也太多了,哼,就对自己的世界如此不满吗。.mp3&prompt_text=最近八重堂穿越异世的小说也太多了,哼,就对自己的世界如此不满吗。&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/bachongshenzi_e8_s256.pth&gweight=GPT_weights/bachongshenzi-e15.ckpt",
    生气: "?text=你好&text_lang=中文&ref_audio_path=yuyin/御姐音/八重神子/生气/6.下雪的时候…_未生灵智的小狐狸,往往会一头栽入雪地来觅食…想看我那样做?哼。.mp3&prompt_text=未生灵智的小狐狸,往往会一头栽入雪地来觅食…想看我那样做?哼。&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/bachongshenzi_e8_s256.pth&gweight=GPT_weights/bachongshenzi-e15.ckpt",
    高兴: "?text=你好&text_lang=中文&ref_audio_path=yuyin/御姐音/八重神子/高兴/39.收到赠礼·其一_哎呀,小家伙,你对我的口味了解得很清楚嘛。.mp3&prompt_text=哎呀,小家伙,你对我的口味了解得很清楚嘛。&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/bachongshenzi_e8_s256.pth&gweight=GPT_weights/bachongshenzi-e15.ckpt",
    呻吟:"?text=你好&text_lang=中文&ref_audio_path=yuyin/御姐音/八重神子/呻吟/65.倒下·其三_止步于此,何谈永恒….mp3&prompt_text=止步于此,何谈永恒…&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/bachongshenzi_e8_s256.pth&gweight=GPT_weights/zhongli-e30.ckpt",
    伤心:"?text=你好&text_lang=中文&ref_audio_path=yuyin/御姐音/八重神子/伤心/36.八重神子的烦恼…_过度安稳的日子实在是太过无聊了啊….mp3&prompt_text=过度安稳的日子实在是太过无聊了啊…&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/bachongshenzi_e8_s256.pth&gweight=GPT_weights/bachongshenzi-e15.ckpt",
  },
  青年音二: {
    闲聊:"?text=你好&text_lang=中文&ref_audio_path=yuyin/青年音二/达达利亚/闲聊/0.初次见面…_我是愚人众执行官第十一席,「公子」达达利亚。而你——哈,也是能招致纷争之人,实在愉快。我们应该会很合得来吧?.mp3&prompt_text=我是愚人众执行官第十一席,「公子」达达利亚。而你——哈,也是能招致纷争之人,实在愉快。我们应该会很合得来吧?&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/dadaliya_e8_s200.pth&gweight=GPT_weights/dadaliya-e15.ckpt",
    生气: "?text=你好&text_lang=中文&ref_audio_path=yuyin/青年音二/达达利亚/生气/20.关于「神之眼」…_只要能让我变强,「神之眼」也好,「邪眼」也好,师承深渊的罪人也好,都无所谓….mp3&prompt_text=只要能让我变强,「神之眼」也好,「邪眼」也好,师承深渊的罪人也好,都无所谓…&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/dadaliya_e8_s200.pth&gweight=GPT_weights/dadaliya-e15.ckpt",
    高兴: "?text=你好&text_lang=中文&ref_audio_path=yuyin/青年音二/达达利亚/高兴/47.收到赠礼·其一_只要吃到好吃的,心情就会十分舒畅。我该如何回报你呢?.mp3&prompt_text=只要吃到好吃的,心情就会十分舒畅。我该如何回报你呢?&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/dadaliya_e8_s200.pth&gweight=GPT_weights/dadaliya-e15.ckptbachongshenzi-e15.ckpt",
    呻吟:"?text=你好&text_lang=中文&ref_audio_path=yuyin/青年音二/达达利亚/呻吟/49.收到赠礼·其三_真是出乎意料的口感,好…我就再试一次吧。.mp3&prompt_text=真是出乎意料的口感,好…我就再试一次吧。&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/dadaliya_e8_s200.pth&gweight=GPT_weights/dadaliya-e15.ckpt",
    伤心:"?text=你好&text_lang=中文&ref_audio_path=yuyin/青年音二/达达利亚/伤心/44.达达利亚的烦恼…_嗯?烦恼的事情…哼,与你同行,总是有值得挑战的强敌,根本没空思考这种琐碎无趣的小事。.mp3&prompt_text=嗯?烦恼的事情…哼,与你同行,总是有值得挑战的强敌,根本没空思考这种琐碎无趣的小事。&prompt_lang=中文&text_split_method=按标点符号切&sweight=SoVITS_weights/dadaliya_e8_s200.pth&gweight=GPT_weights/dadaliya-e15.ckpt",
  },

  // 其他人物...
};





    // 检测并插入播放按钮的函数
    function checkAndInsertPlayButton() {
        const divElements = document.querySelectorAll('div');

        divElements.forEach(div => {
            if (div.getAttribute('dir') === 'auto' && !boundDivs.has(div)) {
                const playButton = document.createElement('button');
                playButton.textContent = 'Play Audio';
                playButton.style.backgroundColor = '#4CAF50'; // 设置按钮背景颜色
                playButton.style.color = 'white'; // 设置按钮文字颜色
                playButton.style.padding = '8px 16px'; // 设置按钮内边距
                playButton.style.border = 'none'; // 移除按钮边框
                playButton.style.borderRadius = '4px'; // 设置按钮圆角
                playButton.style.cursor = 'pointer'; // 设置鼠标指针样式
                div.parentNode.insertBefore(playButton, div.nextSibling);

                playButton.addEventListener('click',function(){
           if(isPlaying==false&&audios.length!=0&&isPlayingDiv==div){
                playnum=0;
                playNextAudio();
            }
            nextPlayingDiv = div;
        });

                boundDivs.add(div); // 将已经绑定过按钮的 div 元素添加到集合中
            }
        });
    }

    // 每隔 5 秒检测一次 div 元素
    setInterval(checkAndInsertPlayButton, 5000);

    //播放div中的音频
   async  function playAudios(){
        if(nextPlayingDiv==""){
            return;
        }
        if(nextPlayingDiv != isPlayingDiv){
        audioQueue.length = 0;
        audios.length=0;
        playnum = 0;
        isPlayingDiv=nextPlayingDiv;
        }

        const div=isPlayingDiv;
        const text = div.textContent.trim();
        const quotes1 = extractQuotes(text);
       // alert(quotes1[1][1]);
        if(quotes1.length==audios.length){

            return;
        }
        let quotes2=[];

        for (let i = audios.length; i <= quotes1.length-1; i++) {
         quotes2.push(quotes1[i]);
         audios.push(quotes1[i]);
       }
        for (const quote of quotes2) {
                 await fetchAudioAndAddToQueue(updateUrlWithText(uRL, quote));
        }
           };
    // 每隔 2秒播放语音
    setInterval(playAudios, 3000);

    // 使用 GM_xmlhttpRequest 获取音频并添加到播放队列
    function fetchAudioAndAddToQueue(url) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: url,
                responseType: 'blob',
                onload: function(response) {
                    if (response.status >= 200 && response.status < 300) {
                        const audioUrl = window.URL.createObjectURL(response.response);
                        audioQueue.push(audioUrl);
                        playNextAudio();
                        resolve();
                    } else {
                        reject(new Error('Failed to load audio'));
                    }
                },
                onerror: function() {
                    reject(new Error('Network error'));
                }
            });
        });
    }

    // 播放队列中的下一个音频
    function playNextAudio() {
        if (isPlaying ||isstop==true) {
            isstop==false;
            return;
        }
        if ( audioQueue.length-1 < playnum ){
            isstop==false;
            return;
        }

        isPlaying = true;
        const audioUrl = audioQueue[playnum]; // 获取队列中的音频URL
        playnum=playnum+1;
        const audio = new Audio(audioUrl);
        audio.play();
        audio.onended = function() {
            isPlaying = false;
            playNextAudio(); // 播放完毕后继续播放下一个
        };
    }

    // 提取文本中的双引号内容
   // function extractQuotes(text) {
       // const quotes = [];
      //  const regex = /"(.*?)"/g;
     //   let match;
    //    while ((match = regex.exec(text)) !== null) {
     //       quotes.push(match[1]);
    //    }
   //     return quotes;
 //   }
  function extractQuotes(text) {
  const regex = /"(\(.+?\))(.+?)"/g;
  const quotes = [];
  let match;
  //alert(text);
  while ((match = regex.exec(text)) !== null) {
    const [_, attributesWithParens, content] = match;
    const attributes = attributesWithParens.slice(1, -1);
    const [character, mood] = attributes.split(',');
    quotes.push([ character, mood, content ]);
   // alert(character);
  //   alert(mood);
  }

   return quotes;
    }

    // 更新 URL 中的 text 参数
    function updateUrlWithText(uRl, quote) {
       // alert(quote[0]);
        const url=uRl+statements[quote[0]][quote[1]];
        const urlObj = new URL(url);
        urlObj.searchParams.set('text', quote[2]);
        return urlObj.toString();
    }
})();