Greasy Fork 还支持 简体中文。

Auto Play Audio Sequentially

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

目前為 2024-04-15 提交的版本,檢視 最新版本

// ==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();
    }
})();