<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>夸克自动转存</title>
  <!-- CSS -->
  <link rel="stylesheet" href="./static/css/bootstrap.min.css">
  <link rel="stylesheet" href="./static/css/bootstrap-icons.min.css">
  <link rel="stylesheet" href="./static/css/dashboard.css">
  <!-- Bootstrap JS -->
  <script src="./static/js/jquery-3.5.1.slim.min.js"></script>
  <script src="./static/js/bootstrap.bundle.min.js"></script>
  <!-- Vue.js -->
  <script src="./static/js/vue@2.js"></script>
  <script src="./static/js/axios.min.js"></script>
  <script src="./static/js/v-jsoneditor.min.js"></script>
</head>

<body>
  <div id="app">

    <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0">
      <a class="navbar-brand col-md-3 col-lg-2 mr-0 px-3" href="#"><i class="bi bi-clouds"></i> 夸克自动转存</a>
      <button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-toggle="collapse" data-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false">
        <span class="navbar-toggler-icon"></span>
      </button>
    </nav>

    <div class="container-fluid">
      <nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
        <div class="sidebar-sticky pt-3">
          <ul class="nav flex-column">
            <li class="nav-item">
              <a class="nav-link" href="#" :class="{active: activeTab === 'config'}" @click="changeTab('config')">
                <i class="bi bi-gear-fill"></i> 系统配置
              </a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#" :class="{active: activeTab === 'tasklist'}" @click="changeTab('tasklist')">
                <i class="bi bi-list-task"></i> 任务列表
              </a>
            </li>
            <h6 class="sidebar-heading px-3 mt-4 mb-1 text-muted">dashboard</h6>
            <li class="nav-item">
              <a class="nav-link" href="/logout">
                <i class="bi bi-box-arrow-right"></i></i> 退出
              </a>
            </li>
          </ul>
          <div class="text-center" style="position: absolute; bottom: 32px; width: 100%; font-size: small;">
            <p><a class="" target="_blank" href="https://github.com/Cp0204/quark-auto-save/wiki"><i class="bi bi-wechat"></i> 使用交流</a></p>
            <p><a target="_blank" href="https://github.com/Cp0204/quark-auto-save"><i class="bi bi-github"></i> quark-auto-save</a></p>
            <p><span v-html="versionTips"></span></p>
          </div>
        </div>
      </nav>

      <main class="col-md-9 col-lg-10 ml-sm-auto">
        <form @submit.prevent="saveConfig" @keydown.enter.prevent>

          <div v-if="activeTab === 'config'">
            <div class="row title">
              <div class="col-10">
                <h2><i class="bi bi-cookie"></i> Cookie</h2>
              </div>
              <div class="col-2 text-right">
                <button type="button" class="btn btn-outline-primary" @click="addCookie()">+</button>
              </div>
            </div>
            <p>1. 所有账号执行签到，纯签到只需移动端参数即可！</p>
            <p>2. 仅第一个账号执行转存，请自行确认顺序。<b>最好是手机验证码<a target="_blank" href="https://pan.quark.cn/">登录</a>，CK比较完整！</b>如需签到参数附在CK后面。</p>
            <div v-for="(value, index) in formData.cookie" :key="index" class="input-group mb-2">
              <input type="text" v-model="formData.cookie[index]" class="form-control" placeholder="打开 pan.quark.com 按 F12 抓取">
              <div class="input-group-append">
                <button type="button" class="btn btn-outline-danger" @click="removeCookie(index)">-</button>
              </div>
            </div>

            <div class="row title">
              <div class="col">
                <h2 style="display: inline-block;"><i class="bi bi-clock"></i> 定时规则</h2>
                <span class="badge badge-pill badge-light">
                  <a target="_blank" href="https://tool.lu/crontab/" title="CRON时间计算器">?</a>
                </span>
              </div>
            </div>
            <div class="input-group mb-2">
              <div class="input-group-prepend">
                <span class="input-group-text">Crontab</span>
              </div>
              <input type="text" v-model="formData.crontab" class="form-control" placeholder="必填">
            </div>

            <div class="row title" title="通知推送，支持多个渠道，见Wiki">
              <div class="col-10">
                <h2 style="display: inline-block;"><i class="bi bi-bell"></i> 通知</h2>
                <span class="badge badge-pill badge-light">
                  <a href="https://github.com/Cp0204/quark-auto-save/wiki/通知推送服务配置" target="_blank">?</a>
                </span>
              </div>
              <div class="col-2 text-right">
                <button type="button" class="btn btn-outline-primary" @click="addPush()">+</button>
              </div>
            </div>
            <div v-for="(value, key) in formData.push_config" :key="key" class="input-group mb-2">
              <div class="input-group-prepend">
                <span class="input-group-text" v-html="key"></span>
              </div>
              <div class="input-group-prepend" v-if="(key == 'DEER_KEY' || key == 'PUSH_KEY')">
                <a type="button" class="btn btn-warning" target="_blank" href="https://sct.ftqq.com/r/13249" title="Server酱推荐计划"><i class="bi bi-award"></i></a>
              </div>
              <input type="text" v-model="formData.push_config[key]" class="form-control">
              <div class="input-group-append">
                <button type="button" class="btn btn-outline-danger" @click="removePush(key)">-</button>
              </div>
            </div>

            <div class="row title" v-if="Object.keys(getAvailablePlugins(formData.plugins)).length" title="各插件的配置选项，具体键值由插件定义，见Wiki">
              <div class="col">
                <h2 style="display: inline-block;"><i class="bi bi-plug"></i> 插件</h2>
                <span class="badge badge-pill badge-light">
                  <a href="https://github.com/Cp0204/quark-auto-save/wiki/插件配置" target="_blank">?</a>
                </span>
              </div>
            </div>
            <div v-for="(plugin, pluginName) in getAvailablePlugins(formData.plugins)" :key="pluginName">
              <div class="form-group row mb-0" style="display:flex; align-items:center;">
                <div data-toggle="collapse" :data-target="'#collapse_'+pluginName" aria-expanded="true" :aria-controls="'collapse_'+pluginName">
                  <div class="btn btn-block text-left">
                    <i class="bi bi-caret-right-fill"></i> <span v-html="`${pluginName}`"></span>
                  </div>
                </div>
              </div>
              <div class="collapse ml-3" :id="'collapse_'+pluginName">
                <div v-for="(value, key) in plugin" :key="key" class="form-group row">
                  <label class="col-sm-2 col-form-label" v-html="key"></label>
                  <div class="col-sm-10">
                    <input type="text" v-model="formData.plugins[pluginName][key]" class="form-control">
                  </div>
                </div>
              </div>
            </div>

            <div class="row title" title="预定义的正则匹配规则，在任务列表中可直接点击使用">
              <div class="col-10">
                <h2 style="display: inline-block;"><i class="bi bi-magic"></i> 魔法匹配</h2>
                <span class="badge badge-pill badge-light">
                  <a href="https://github.com/Cp0204/quark-auto-save/wiki/正则处理教程#21-魔法匹配" target="_blank">?</a>
                </span>
              </div>
              <div class="col-2 text-right">
                <button type="button" class="btn btn-outline-primary" @click="addMagicRegex()">+</button>
              </div>
            </div>
            <div v-for="(value, key) in formData.magic_regex" :key="key" class="form-group mb-2">
              <div class="input-group">
                <div class="input-group-prepend">
                  <span class="input-group-text">魔法名</span>
                </div>
                <input type="text" :data-oldkey="key" v-model="key" class="form-control" @change="updateMagicRegexKey($event.target.dataset.oldkey, $event.target.value)" placeholder="自定义名称">
                <div class="input-group-prepend">
                  <span class="input-group-text">正则命名</span>
                </div>
                <input type="text" v-model="value.pattern" class="form-control" placeholder="匹配表达式">
                <input type="text" v-model="value.replace" class="form-control" placeholder="替换表达式">
                <div class="input-group-append">
                  <button type="button" class="btn btn-outline-danger" @click="removeMagicRegex(key)">-</button>
                </div>
              </div>
            </div>

            <!-- 剧集识别模块 -->
            <div class="row title" title="识别文件名中的剧集编号，用于自动重命名">
              <div class="col-10">
                <h2 style="display: inline-block;"><i class="bi bi-list-ol"></i> 剧集识别</h2>
                <span class="badge badge-pill badge-light">
                  <a href="#" target="_blank" @click.prevent="alert('剧集识别说明：\n在任务配置中填写包含E[]的匹配表达式，如「剧名 - S01E[]」，将自动切换至剧集命名模式，自动识别文件名中的编号作为集编号，并替换[]部分。\n\n集编号匹配规则：\n用于识别集编号的正则表达式，多个表达式之间用竖线「|」分隔，每个表达式必须包含一个捕获组，用于提取剧集编号')">?</a>
                </span>
              </div>
            </div>
            <div class="form-group">
              <div class="input-group">
                <div class="input-group-prepend">
                  <span class="input-group-text">集编号识别规则</span>
                </div>
                <input type="text" class="form-control" v-model="episodePatternsText" placeholder="多个正则表达式用竖线「|」分隔，如：(\\d+)|[Ee](\\d+)|第(\\d+)集">
              </div>
            </div>

            <div class="row title" title="API接口，用以第三方添加任务等操作，见Wiki">
              <div class="col-10">
                <h2 style="display: inline-block;"><i class="bi bi-link-45deg"></i> API</h2>
                <span class="badge badge-pill badge-light">
                  <a href="https://github.com/Cp0204/quark-auto-save/wiki/API接口" target="_blank">?</a>
                </span>
              </div>
            </div>
            <div class="input-group">
              <div class="input-group-prepend">
                <span class="input-group-text">Token</span>
              </div>
              <input type="text" v-model="formData.api_token" class="form-control" style="background-color:white;" disabled>
            </div>

            <div class="row title" title="资源搜索服务配置，用于任务名称智能搜索">
              <div class="col-10">
                <h2 style="display: inline-block;"><i class="bi bi-search"></i> CloudSaver</h2>
                <span class="badge badge-pill badge-light">
                  <a href="https://github.com/Cp0204/quark-auto-save/wiki/CloudSaver搜索源" target="_blank">?</a>
                </span>
              </div>
            </div>
            <div class="form-group row">
              <label class="col-sm-2 col-form-label">服务器</label>
              <div class="col-sm-10">
                <input type="text" v-model="formData.source.cloudsaver.server" class="form-control" placeholder="资源搜索服务器地址，如 http://172.17.0.1:8008">
              </div>
            </div>
            <div class="form-group row">
              <label class="col-sm-2 col-form-label">用户名</label>
              <div class="col-sm-10">
                <input type="text" v-model="formData.source.cloudsaver.username" class="form-control" placeholder="用户名">
              </div>
            </div>
            <div class="form-group row">
              <label class="col-sm-2 col-form-label">密码</label>
              <div class="col-sm-10">
                <input type="password" v-model="formData.source.cloudsaver.password" class="form-control" placeholder="密码">
              </div>
            </div>

          </div>

          <div v-if="activeTab === 'tasklist'">
            <div class="row title">
              <div class="col">
                <h2>任务列表</h2>
              </div>
            </div>
            <hr>
            <div class="row">
              <div class="col-lg-6 col-md-12">
                <div class="row">
                  <label class="col-form-label col-md-3">名称筛选</label>
                  <div class="input-group col-md-9">
                    <input type="text" class="form-control" v-model="taskNameFilter" placeholder="名称 筛选/搜索">
                    <div class="input-group-append">
                      <button type="button" class="btn btn-outline-secondary" @click="clearData('taskNameFilter')"><i class="bi bi-x-lg"></i></button>
                    </div>
                  </div>
                </div>
              </div>
              <div class="col-lg-6 col-md-12">
                <div class="row">
                  <label class="col-form-label col-md-3">路径筛选</label>
                  <div class="input-group col-md-9">
                    <select class="form-control" v-model="taskDirSelected">
                      <option v-for="(dir, index) in taskDirs" :value="dir" v-html="dir"></option>
                    </select>
                    <div class="input-group-append">
                      <button type="button" class="btn btn-outline-secondary" @click="clearData('taskDirSelected')"><i class="bi bi-x-lg"></i></button>
                    </div>
                  </div>
                </div>
              </div>
            </div>
            <div v-for="(task, index) in formData.tasklist" :key="index" class="task mb-3">
              <template v-if="(taskDirSelected == '' || getParentDirectory(task.savepath) == taskDirSelected) && task.taskname.includes(taskNameFilter)">
                <hr>
                <div class="form-group row" style="align-items:center">
                  <div class="col pl-0" data-toggle="collapse" :data-target="'#collapse_'+index" aria-expanded="true" :aria-controls="'collapse_'+index">
                    <div class="btn btn-block text-left">
                      <i class="bi bi-caret-right-fill"></i> #<span v-html="`${index+1}: ${task.taskname}`"></span>
                    </div>
                  </div>
                  <div class="col-auto">
                    <button class="btn btn-warning" v-if="task.shareurl_ban" :title="task.shareurl_ban" disabled><i class="bi bi-exclamation-triangle-fill"></i></button>
                    <button type="button" class="btn btn-outline-primary" @click="runScriptNow(index)" title="运行此任务" v-else><i class="bi bi-play-fill"></i></button>
                    <button type="button" class="btn btn-outline-danger" @click="removeTask(index)" title="删除此任务"><i class="bi bi-trash3-fill"></i></button>
                  </div>
                </div>
                <div class="collapse ml-3" :id="'collapse_'+index">
                  <div class="alert alert-warning" role="alert" v-if="task.shareurl_ban" v-html="task.shareurl_ban"></div>
                  <div class="form-group row">
                    <label class="col-sm-2 col-form-label">任务名称</label>
                    <div class="col-sm-10">
                      <div class="input-group">
                        <input type="text" name="taskname[]" class="form-control" v-model="task.taskname" placeholder="必填" @focus="smart_param.showSuggestions=true;focusTaskname(index, task)" @input="changeTaskname(index, task)">
                        <div class="dropdown-menu show task-suggestions" v-if="smart_param.showSuggestions && smart_param.taskSuggestions.success && smart_param.index === index">
                          <div class="dropdown-item text-muted text-center" style="font-size:12px;">{{ smart_param.taskSuggestions.message ? smart_param.taskSuggestions.message : smart_param.taskSuggestions.data.length ? `以下资源来自 ${smart_param.taskSuggestions.source} 搜索，请自行辨识，如有侵权请联系资源方` : "未搜索到资源" }}</div>
                          <div v-for="suggestion in smart_param.taskSuggestions.data" :key="suggestion.taskname" class="dropdown-item cursor-pointer" @click.prevent="selectSuggestion(index, suggestion)" style="font-size: 12px;" :title="suggestion.content">
                            <span v-html="suggestion.verify ? '✅': '❔'"></span> {{ suggestion.taskname }}
                            <small class="text-muted">
                              <a :href="suggestion.shareurl" target="_blank" @click.stop>{{ suggestion.shareurl }}</a>
                            </small>
                          </div>
                        </div>
                        <div class="input-group-append" title="深度搜索">
                          <button class="btn btn-primary" type="button" @click="searchSuggestions(index, task.taskname)">
                            <i v-if="smart_param.isSearching && smart_param.index === index" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></i>
                            <i v-else class="bi bi-search-heart"></i>
                          </button>
                          <div class="input-group-text" title="谷歌搜索">
                            <a target="_blank" :href="`https://www.google.com/search?q=%22pan.quark.cn/s%22+${task.taskname}`"><i class="bi bi-google"></i></a>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                  <div class="form-group row" title="支持子目录链接，Web端打开分享点入目录，复制浏览器的URL即可；支持带提取码链接，说明见Wiki">
                    <label class="col-sm-2 col-form-label">分享链接</label>
                    <div class="col-sm-10">
                      <div class="input-group">
                        <input type="text" name="shareurl[]" class="form-control" v-model="task.shareurl" placeholder="必填" @blur="changeShareurl(task)">
                        <div class="input-group-append" v-if="task.shareurl">
                          <button type="button" class="btn btn-outline-secondary" @click="fileSelect.selectDir=true;fileSelect.previewRegex=false;showShareSelect(index)" title="选择文件夹"><i class="bi bi-folder"></i></button>
                          <div class="input-group-text">
                            <a target="_blank" :href="task.shareurl"><i class="bi bi-box-arrow-up-right"></i></a>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                  <div class="form-group row">
                    <label class="col-sm-2 col-form-label">保存路径</label>
                    <div class="col-sm-10">
                      <div class="input-group">
                        <input type="text" name="savepath[]" class="form-control" v-model="task.savepath" placeholder="必填" @focus="focusTaskname(index, task)">
                        <div class="input-group-append">
                          <button class="btn btn-secondary" type="button" v-if="smart_param.savepath && smart_param.index == index && task.savepath != smart_param.origin_savepath" @click="task.savepath = smart_param.origin_savepath"><i class="bi bi-reply"></i></button>
                          <button class="btn btn-outline-secondary" type="button" @click="showSavepathSelect(index)">选择</button>
                        </div>
                      </div>
                    </div>
                  </div>
                  <div class="form-group row" :title="task.use_sequence_naming || task.use_episode_naming ? (task.use_sequence_naming ? '输入带{}占位符的重命名格式，如剧名 - S01E{}、剧名.S03E{}等，{}将被替换为集序号（按文件名和上传时间智能排序），目录中的文件夹会被自动过滤' : '输入带[]占位符的重命名格式，如剧名 - S01E[]、剧名.S03E[]等，[]将被替换为从文件名中提取的集编号，目录中的文件夹会被自动过滤') : '可用作筛选，只转存匹配到的文件名的文件，留空则转存所有文件'">
                    <label class="col-sm-2 col-form-label">保存规则</label>
                    <div class="col-sm-10">
                      <div class="input-group">
                        <div class="input-group-prepend">
                          <button class="btn btn-outline-secondary" type="button" @click="fileSelect.selectDir=true;fileSelect.previewRegex=true;showShareSelect(index)" title="预览正则命名效果">
                            {{ task.use_sequence_naming ? '顺序命名' : (task.use_episode_naming ? '剧集命名' : '正则命名') }}
                          </button>
                        </div>
                        <input type="text" name="pattern[]" class="form-control" v-model="task.pattern" :placeholder="task.use_sequence_naming ? '输入带{}占位符的重命名格式，如剧名 - S01E{}' : (task.use_episode_naming ? '输入带[]占位符的重命名格式，如剧名 - S01E[]' : '匹配表达式')"  list="magicRegex" @input="detectNamingMode(task)">
                        <input v-if="!task.use_sequence_naming && !task.use_episode_naming" type="text" name="replace[]" class="form-control" v-model="task.replace" placeholder="替换表达式">
                        <input v-else type="text" class="form-control" disabled value="" style="background-color: #e9ecef;">
                        <div class="input-group-append" title="保存时只比较文件名的部分，01.mp4 和 01.mkv 视同为同一文件，不重复转存">
                          <div class="input-group-text">
                            <input type="checkbox" v-model="task.ignore_extension">&nbsp;忽略后缀
                          </div>
                        </div>
                      </div>
                      <datalist id="magicRegex">
                        <option v-for="(value, key) in formData.magic_regex" :key="key" :value="`${key}`" v-html="`${value.pattern.replace('<', '<\u200B')} → ${value.replace}`"></option>
                      </datalist>
                    </div>
                  </div>
                  
                  <div class="form-group row" title="名称包含过滤词汇的项目不会被转存，多个词用逗号分隔，支持通过文件名和扩展名过滤文件和文件夹">
                    <label class="col-sm-2 col-form-label">过滤规则</label>
                    <div class="col-sm-10">
                      <div class="input-group">
                        <input type="text" name="filterwords[]" class="form-control" v-model="task.filterwords" placeholder="可选，输入过滤词汇，用逗号分隔，如：纯享，txt，超前企划，名称包含过滤词汇的项目不会被转存">
                      </div>
                    </div>
                  </div>
                  
                  <div class="form-group row" title="只转存修改日期>选中文件的文件，在容量不够或几百集动漫的场景下非常有用">
                    <label class="col-sm-2 col-form-label">文件起始</label>
                    <div class="col-sm-10">
                      <div class="input-group">
                        <input type="text" class="form-control" placeholder="可选，只转存修改日期>此文件的文件" name="startfid[]" v-model="task.startfid">
                        <div class="input-group-append" v-if="task.shareurl">
                          <button class="btn btn-outline-secondary" type="button" @click="fileSelect.selectDir=false;fileSelect.previewRegex=false;showShareSelect(index)">选择</button>
                        </div>
                      </div>
                    </div>
                  </div>
                  <div class="form-group row" title="需匹配到各级嵌套目录名才会更新，否则子目录在第一次转存后不会更新。注意：原理是逐级索引，深层嵌套目录的场景下效率非常低，慎用 .*">
                    <label class="col-sm-2 col-form-label">更新目录</label>
                    <div class="col-sm-10">
                      <input type="text" name="update_subdir[]" class="form-control" v-model="task.update_subdir" placeholder="可选，匹配需更新子目录（含各级嵌套目录）的正则表达式，多项以|分割，如 4k|1080p">
                    </div>
                  </div>
                  <div class="form-group row">
                    <label class="col-sm-2 col-form-label">截止日期</label>
                    <div class="col-sm-10">
                      <input type="date" name="enddate[]" class="form-control" v-model="task.enddate" placeholder="可选">
                    </div>
                  </div>
                  <div class="form-group row" title="只在勾选的星期时才运行，在某些周更剧的场景下非常有用">
                    <label class="col-sm-2 col-form-label">运行星期</label>
                    <div class="col-sm-10 col-form-label">
                      <div class="form-check form-check-inline" title="也可用作任务总开关">
                        <input class="form-check-input" type="checkbox" :checked="task.runweek.length === 7" @change="toggleAllWeekdays(task)" :indeterminate.prop="task.runweek.length > 0 && task.runweek.length < 7">
                        <label class="form-check-label">全选</label>
                      </div>
                      <div class="form-check form-check-inline" v-for="(day, index) in weekdays" :key="index">
                        <input class="form-check-input" type="checkbox" v-model="task.runweek" :value="index+1">
                        <label class="form-check-label" v-html="day"></label>
                      </div>
                    </div>
                  </div>
                  <div class="form-group row" v-if="Object.keys(getAvailablePlugins(formData.plugins)).length" title="单个任务的插件选项，具体键值由插件定义，见Wiki">
                    <label class="col-sm-2 col-form-label">插件选项</label>
                    <div class="col-sm-10">
                      <v-jsoneditor v-model="task.addition" :options="{mode:'tree'}" :plus="false" height="180px"></v-jsoneditor>
                    </div>
                  </div>
                </div>
              </template>
            </div>
            <div class="row mt-5">
              <div class="col-sm-12 text-center">
                <button type="button" class="btn btn-primary" @click="addTask()"><i class="bi bi-plus"></i> 增加任务</button>
              </div>
            </div>
          </div>

          <div class="bottom-buttons">
            <button class="btn btn-success" title="保存 CTRL+S"><i class="bi bi-floppy2-fill"></i></button>
            <button type="button" class="btn btn-primary" title="运行 CTRL+R" @click="runScriptNow()"><i class="bi bi-play-fill"></i></button>
            <button type="button" class="btn btn-info" @click="scrollToX(0)" @dblclick="scrollToX()" data-toggle="tooltip" data-placement="top" title="单击回顶，双击到底"><i class="bi bi-chevron-bar-up"></i></button>
          </div>
        </form>
      </main>
    </div>

    <!-- 模态框 运行日志 -->
    <div class="modal" tabindex="-1" id="logModal">
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title"><b>运行日志</b>
              <div v-if="modalLoading" class="spinner-border spinner-border-sm m-1" role="status"></div>
            </h5>
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
          <div class="modal-body">
            <pre v-html="run_log"></pre>
          </div>
        </div>
      </div>
    </div>

    <!-- 模态框 文件选择 -->
    <div class="modal" tabindex="-1" id="fileSelectModal">
      <div class="modal-dialog modal-lg">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title">
              <b v-if="fileSelect.previewRegex">{{ formData.tasklist[fileSelect.index].use_sequence_naming ? '顺序命名预览' : (formData.tasklist[fileSelect.index].use_episode_naming ? '剧集命名预览' : '正则命名预览') }}</b>
              <b v-else-if="fileSelect.selectDir">选择{{fileSelect.selectShare ? '需转存的' : '保存到的'}}文件夹</b>
              <b v-else>选择起始文件</b>
              <div v-if="modalLoading" class="spinner-border spinner-border-sm m-1" role="status"></div>
            </h5>
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
          <div class="modal-body small">
            <div class="alert alert-warning" v-if="fileSelect.error" v-html="fileSelect.error"></div>
            <div v-else>
              <!-- 面包屑导航 -->
              <nav aria-label="breadcrumb" v-if="fileSelect.selectDir">
                <ol class="breadcrumb">
                  <li class="breadcrumb-item cursor-pointer" @click="navigateTo('0','/')"><i class="bi bi-house-door"></i></li>
                  <li v-for="(item, index) in fileSelect.paths" class="breadcrumb-item">
                    <a v-if="index != fileSelect.paths.length - 1" href="#" @click="navigateTo(item.fid, item.name)">{{ item.name }}</a>
                    <span v-else class="text-muted">{{ item.name }}</span>
                  </li>
                </ol>
              </nav>
              <!-- 文件列表 -->
              <div class="mb-3" v-if="fileSelect.previewRegex">
                <div v-if="formData.tasklist[fileSelect.index].use_sequence_naming">
                  <b>顺序命名格式：</b><span class="badge badge-info" v-html="formData.tasklist[fileSelect.index].pattern"></span>
                </div>
                <div v-else-if="formData.tasklist[fileSelect.index].use_episode_naming">
                  <b>剧集命名格式：</b><span class="badge badge-info" v-html="formData.tasklist[fileSelect.index].pattern"></span>
                </div>
                <div v-else>
                  <b>匹配表达式：</b><span class="badge badge-info" v-html="formData.tasklist[fileSelect.index].pattern"></span>
                  <b>替换表达式：</b><span class="badge badge-info" v-html="formData.tasklist[fileSelect.index].replace"></span>
                </div>
              </div>
              <table class="table table-hover table-sm">
                <thead>
                  <tr>
                    <th scope="col">文件名</th>
                    <th scope="col" v-if="fileSelect.selectShare">{{ fileSelect.index !== null && formData.tasklist[fileSelect.index] ? 
                      (formData.tasklist[fileSelect.index].use_sequence_naming ? '顺序命名' : 
                      (formData.tasklist[fileSelect.index].use_episode_naming ? '剧集命名' : '正则命名')) : '重命名' }}</th>
                    <template v-if="!fileSelect.previewRegex">
                      <th scope="col">大小</th>
                      <th scope="col">修改日期 ↓</th>
                      <th scope="col" v-if="!fileSelect.selectShare">操作</th>
                    </template>
                  </tr>
                </thead>
                <tbody>
                  <tr v-for="(file, key) in fileSelect.fileList" :key="key" @click="fileSelect.selectDir ? (file.dir ? navigateTo(file.fid, file.file_name) : null) : selectStartFid(file.fid)" :class="{'cursor-pointer': fileSelect.selectDir ? file.dir : true}">
                    <td><i class="bi" :class="file.dir ? 'bi-folder-fill text-warning' : 'bi-file-earmark'"></i> {{file.file_name}}</td>
                    <td v-if="fileSelect.selectShare" :class="file.file_name_re ? 'text-success' : 'text-danger'">{{file.file_name_re || '&times;'}}</td>
                    <template v-if="!fileSelect.previewRegex">
                      <td v-if="file.dir">{{ file.include_items }}项</td>
                      <td v-else>{{file.size | size}}</td>
                      <td>{{file.updated_at | ts2date}}</td>
                      <td v-if="!fileSelect.selectShare"><a @click.stop.prevent="deleteFile(file.fid, file.file_name, file.dir)">删除</a></td>
                    </template>
                  </tr>
                </tbody>
              </table>
            </div>
          </div>
          <div class="modal-footer" v-if="fileSelect.selectDir && !fileSelect.previewRegex">
            <span v-html="fileSelect.selectShare ? '转存：' : '保存到：'"></span>
            <button type="button" class="btn btn-primary btn-sm" @click="selectCurrentFolder()">当前文件夹</button>
            <button type="button" class="btn btn-primary btn-sm" v-if="!fileSelect.selectShare" @click="selectCurrentFolder(true)">当前文件夹<span class="badge badge-light" v-html="'/'+formData.tasklist[fileSelect.index].taskname"></span></button>
          </div>
        </div>
      </div>
    </div>
  </div>


  <script>
    var app = new Vue({
      el: '#app',
      data: {
        version: "[[ version ]]",
        versionTips: "",
        plugin_flags: "[[ plugin_flags ]]",
        weekdays: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
        formData: {
          cookie: [],
          push_config: {},
          media_servers: {},
          tasklist: [],
          magic_regex: {},
          episode_patterns: [],
          source: {
            cloudsaver: {
              server: "",
              username: "",
              password: "",
              token: ""
            }
          },
        },
        newTask: {
          taskname: "",
          shareurl: "",
          savepath: "/",
          pattern: "",
          replace: "",
          enddate: "",
          addition: {},
          ignore_extension: false,
          filterwords: "",
          runweek: [1, 2, 3, 4, 5, 6, 7],
          sequence_naming: "",
          use_sequence_naming: false,
          episode_naming: "",
          use_episode_naming: false
        },
        run_log: "",
        taskDirs: [""],
        taskDirSelected: "",
        taskNameFilter: "",
        modalLoading: false,
        smart_param: {
          index: null,
          savepath: "",
          origin_savepath: "",
          taskSuggestions: {},
          showSuggestions: false,
          isSearching: false,
          searchTimer: null,
        },
        activeTab: 'config',
        configModified: false,
        fileSelect: {
          index: null,
          shareurl: "",
          stoken: "",
          fileList: [],
          paths: [],
          selectDir: true,
          selectShare: true,
          previewRegex: false,
        },
      },
      computed: {
        episodePatternsText: {
          get() {
            if (!this.formData.episode_patterns) return '';
            return this.formData.episode_patterns.map(p => p.regex || '').join('|');
          },
          set(value) {
            // 将文本拆分为竖线分隔的表达式，每个创建一个正则对象
            const patterns = value.split('|').filter(pattern => pattern.trim() !== '');
            this.formData.episode_patterns = patterns.map(pattern => ({
              regex: pattern.trim()
            }));
          }
        }
      },
      filters: {
        ts2date: function (value) {
          const date = new Date(value);
          return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`;
        },
        size: function (value) {
          if (!value) return "";
          const unitArr = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
          const srcsize = parseFloat(value);
          const index = srcsize ? Math.floor(Math.log(srcsize) / Math.log(1024)) : 0;
          const size = (srcsize / Math.pow(1024, index)).toFixed(1).replace(/\.?0+$/, "");
          return size + unitArr[index];
        }
      },
      watch: {
        formData: {
          handler(newVal, oldVal) {
            this.configModified = true;
          },
          deep: true
        }
      },
      mounted() {
        this.fetchData();
        this.checkNewVersion();
        
        // 从本地存储中恢复之前的标签页状态
        const savedTab = localStorage.getItem('quarkAutoSave_activeTab');
        if (savedTab) {
          this.activeTab = savedTab;
        }
        
        $('[data-toggle="tooltip"]').tooltip();
        document.addEventListener('keydown', this.handleKeyDown);
        document.addEventListener('click', (e) => {
          if (!e.target.closest('.input-group')) {
            this.smart_param.showSuggestions = false;
          }
        });
        window.addEventListener('beforeunload', this.handleBeforeUnload);
        
        // 初始化时检查所有任务的命名模式
        setTimeout(() => {
          if (this.formData.tasklist && this.formData.tasklist.length > 0) {
            this.formData.tasklist.forEach(task => {
              // 检查现有的顺序命名设置
              if (task.use_sequence_naming && task.sequence_naming) {
                // 已经设置过顺序命名的，将顺序命名模式转换为匹配表达式
                if (!task.pattern || task._pattern_backup) {
                  task.pattern = task.sequence_naming;
                }
              } else if (task.use_episode_naming && task.episode_naming) {
                // 已经设置过剧集命名的，将剧集命名模式转换为匹配表达式
                if (!task.pattern || task._pattern_backup) {
                  task.pattern = task.episode_naming;
                }
              } else {
                // 检测是否包含顺序命名或剧集命名模式
                this.detectNamingMode(task);
              }
            });
          }
          
          // 如果没有剧集识别模式，添加默认模式
          if (!this.formData.episode_patterns || this.formData.episode_patterns.length === 0) {
            this.formData.episode_patterns = [
              { name: 'EP_BASIC', description: '[]', regex: '(\\d+)' },
              { name: 'EP_4K', description: '[]-4K', regex: '(\\d+)[-_\\s]*4[Kk]' },
              { name: 'EP_HUA', description: '[]话', regex: '(\\d+)话' },
              { name: 'EP_E', description: 'E[]', regex: '[Ee](\\d+)' },
              { name: 'EP_EP', description: 'EP[]', regex: '[Ee][Pp](\\d+)' },
              { name: 'EP_DIHUA', description: '第[]话', regex: '第(\\d+)话' },
              { name: 'EP_DIJI', description: '第[]集', regex: '第(\\d+)集' },
              { name: 'EP_DIQI', description: '第[]期', regex: '第(\\d+)期' },
              { name: 'EP_4KSPACE', description: '[] 4K', regex: '(\\d+)\\s+4[Kk]' },
              { name: 'EP_4KUNDER', description: '[]_4K', regex: '(\\d+)[_\\s]4[Kk]' },
              { name: 'EP_BRACKET', description: '【[]】', regex: '【(\\d+)】' },
              { name: 'EP_SQUAREBRACKET', description: '方括号数字', regex: '\\[(\\d+)\\]' },
              { name: 'EP_UNDERSCORE', description: '_[]_', regex: '_?(\\d+)_' }
            ];
          }
        }, 500);
      },
      beforeDestroy() {
        window.removeEventListener('beforeunload', this.handleBeforeUnload);
      },
      methods: {
        changeTab(tab) {
          this.activeTab = tab;
          // 在本地存储中保存当前标签页状态
          localStorage.setItem('quarkAutoSave_activeTab', tab);
          if (window.innerWidth <= 768) {
            $('#sidebarMenu').collapse('toggle')
          }
        },
        checkNewVersion() {
          this.versionTips = this.version;
          axios.get('https://api.github.com/repos/Cp0204/quark-auto-save/tags')
            .then(response => {
              latestVersion = response.data[0].name;
              console.log(`检查版本：当前 ${this.version} 最新 ${latestVersion}`);
              if (latestVersion != this.version) {
                this.versionTips += ` <sup><span class="badge badge-pill badge-danger">${latestVersion}</span></sup>`;
              }
            })
            .catch(error => {
              console.error('Error:', error);
            });
        },
        fetchData() {
          axios.get('/data')
            .then(response => {
              config_data = response.data.data
              // cookie兼容
              if (typeof config_data.cookie === 'string')
                config_data.cookie = [config_data.cookie];
              // 添加星期预设
              config_data.tasklist = config_data.tasklist.map(task => {
                if (!task.hasOwnProperty('runweek')) {
                  task.runweek = [1, 2, 3, 4, 5, 6, 7];
                }
                return task;
              });
              // 获取所有任务父目录
              config_data.tasklist.forEach(item => {
                parentDir = this.getParentDirectory(item.savepath)
                if (!this.taskDirs.includes(parentDir))
                  this.taskDirs.push(parentDir);
              });
              this.newTask.addition = config_data.task_plugins_config_default;
              // 确保source配置存在
              if (!config_data.source) {
                config_data.source = {};
              }
              if (!config_data.source.cloudsaver) {
                config_data.source.cloudsaver = {
                  server: "",
                  username: "",
                  password: "",
                  token: ""
                };
              }
              // 确保剧集识别模式存在
              if (!config_data.episode_patterns) {
                config_data.episode_patterns = [];
              }
              this.formData = config_data;
              setTimeout(() => {
                this.configModified = false;
              }, 100);
            })
            .catch(error => {
              console.error('Error fetching data:', error);
            });
        },
        handleKeyDown(event) {
          if (event.ctrlKey || event.metaKey) {
            if (event.keyCode === 83 || event.key === 's') {
              event.preventDefault();
              this.saveConfig();
            } else if (event.keyCode === 82 || event.key === 'r') {
              event.preventDefault();
              this.runScriptNow();
            }
          }
        },
        handleBeforeUnload(e) {
          if (this.configModified) {
            e.preventDefault();
            e.returnValue = '配置已修改但未保存，确定要离开吗？';
            return e.returnValue;
          }
        },
        saveConfig() {
          // 保存前处理每个任务的命名模式
          if (this.formData.tasklist && this.formData.tasklist.length > 0) {
            this.formData.tasklist.forEach(task => {
              // 如果是顺序命名模式，确保sequence_naming字段已正确设置
              if (task.use_sequence_naming && task.pattern && task.pattern.includes('{}')) {
                task.sequence_naming = task.pattern;
              }
              // 如果是剧集命名模式，确保episode_naming字段已正确设置
              if (task.use_episode_naming && task.pattern && task.pattern.includes('[]')) {
                task.episode_naming = task.pattern;
              }
            });
          }
          
          axios.post('/update', this.formData)
            .then(response => {
              if (response.data.success) {
                this.configModified = false;
                alert(response.data.message);
              } else {
                alert(response.data.message);
              }
              console.log('Config saved result:', response.data);
            })
            .catch(error => {
              console.error('Error saving config:', error);
            });
        },
        addCookie() {
          this.formData.cookie.push("");
        },
        removeCookie(index) {
          if (this.formData.cookie[index] == "" || confirm("确认删除吗？"))
            this.formData.cookie.splice(index, 1);
        },
        addPush() {
          key = prompt("增加的键名", "");
          if (key != "" && key != null)
            this.$set(this.formData.push_config, key, "");
        },
        removePush(key) {
          if (confirm("确认删除吗？"))
            this.$delete(this.formData.push_config, key);
        },
        addTask() {
          newTask = { ...this.newTask }
          newTask.taskname = this.taskNameFilter;
          if (this.formData.tasklist.length > 0) {
            lastTask = this.formData.tasklist[this.formData.tasklist.length - 1];
            if (this.taskDirSelected) {
              newTask.savepath = this.taskDirSelected + '/TASKNAME';
            } else {
              if (newTask.taskname) {
                newTask.savepath = lastTask.savepath.replace(lastTask.taskname, newTask.taskname);
              } else {
                newTask.savepath = lastTask.taskname ? lastTask.savepath.replace(lastTask.taskname, 'TASKNAME') : lastTask.savepath;
              }
            }
          }
          
          // 初始化新任务的命名模式相关字段
          if (newTask.taskname) {
            // 默认使用正则命名模式
            newTask.pattern = ".*";  // 默认匹配所有文件
            newTask.replace = "";    // 默认保持原文件名
            newTask.use_sequence_naming = false;
            newTask.sequence_naming = "";
          }
          
          this.formData.tasklist.push(newTask);
          
          // 滚到最下
          setTimeout(() => {
            $('#collapse_' + (this.formData.tasklist.length - 1)).collapse('show').on('shown.bs.collapse', () => {
              this.scrollToX();
            });
          }, 1);
        },
        focusTaskname(index, task) {
          this.smart_param.index = index
          this.smart_param.origin_savepath = task.savepath
          regex = new RegExp(`/${task.taskname.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(/|$)`)
          if (task.savepath.includes('TASKNAME')) {
            this.smart_param.savepath = task.savepath;
          } else if (task.savepath.match(regex)) {
            this.smart_param.savepath = task.savepath.replace(task.taskname, 'TASKNAME');
          } else {
            this.smart_param.savepath = undefined;
          }
        },
        changeTaskname(index, task) {
          if (this.smart_param.searchTimer) {
            clearTimeout(this.smart_param.searchTimer);
          }
          this.smart_param.searchTimer = setTimeout(() => {
            this.searchSuggestions(index, task.taskname, 0);
          }, 1000);
          if (this.smart_param.savepath)
            task.savepath = this.smart_param.savepath.replace('TASKNAME', task.taskname);
        },
        removeTask(index) {
          if (confirm("确认删除任务 [#" + (index + 1) + ": " + this.formData.tasklist[index].taskname + "] 吗？"))
            this.formData.tasklist.splice(index, 1);
        },
        changeShareurl(task) {
          if (!task.shareurl)
            return;
          this.$set(task, "shareurl_ban", undefined);
          // 从URL中提取任务名
          try {
            const matches = decodeURIComponent(task.shareurl).match(/\/(\w{32})-([^\/]+)$/);
            if (matches) {
              task.taskname = task.taskname == "" ? matches[2] : task.taskname;
              task.savepath = task.savepath.replace(/TASKNAME/g, matches[2]);
            }
          } catch (e) {
            console.error("Error decodeURIComponent:", e);
          }
          // 从分享中提取任务名
          axios.get('/get_share_detail', { params: { shareurl: task.shareurl } })
            .then(response => {
              share_detail = response.data.data
              if (!response.data.success) {
                if (share_detail.error.includes("提取码")) {
                  const passcode = prompt("检查失败[" + share_detail.error + "]，请输入提取码：");
                  if (passcode != null) {
                    task.shareurl = task.shareurl.replace(/pan.quark.cn\/s\/(\w+)(\?pwd=\w*)*/, `pan.quark.cn/s/$1?pwd=${passcode}`);
                    this.changeShareurl(task);
                    return;
                  }
                }
                this.$set(task, "shareurl_ban", share_detail.error);
              } else {
                task.taskname = task.taskname == "" ? share_detail.share.title : task.taskname;
                task.savepath = task.savepath.replace(/TASKNAME/g, share_detail.share.title);
                this.$set(task, "shareurl_ban", undefined);
              }
            })
            .catch(error => {
              console.error('Error get_share_detail:', error);
            });
        },
        clearData(target) {
          this[target] = "";
        },
        async runScriptNow(task_index = null) {
          body = {};
          if (task_index != null) {
            task = { ...this.formData.tasklist[task_index] };
            delete task.runweek;
            delete task.enddate;
            body = {
              "tasklist": [task]
            };
          } else if (this.configModified) {
            if (!confirm('配置已修改但未保存，是否继续运行？')) {
              return;
            }
          }
          $('#logModal').modal('toggle');
          this.modalLoading = true;
          this.run_log = '';
          try {
            // 1. 发送 POST 请求
            const response = await fetch(`/run_script_now`, {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json'
              },
              body: JSON.stringify(body)
            });
            if (!response.ok) {
              throw new Error(`HTTP error! Status: ${response.status}`);
            }
            // 2. 处理 SSE 流
            const reader = response.body.getReader();
            const decoder = new TextDecoder();
            let partialData = '';
            while (true) {
              const { done, value } = await reader.read();
              if (done) {
                console.log('Stream complete.');
                this.modalLoading = false;
                // 运行后刷新数据
                this.fetchData();
                break;
              }
              partialData += decoder.decode(value);
              const lines = partialData.split('\n').filter(line => line.trim() !== '');
              for (const line of lines) {
                if (line.startsWith('data:')) {
                  const eventData = line.substring(5).trim();
                  if (eventData === '[DONE]') {
                    this.modalLoading = false;
                    this.fetchData();
                    return;
                  }
                  this.run_log += eventData + '\n';
                  // 在更新 run_log 后将滚动条滚动到底部
                  this.$nextTick(() => {
                    const modalBody = document.querySelector('.modal-body');
                    modalBody.scrollTop = modalBody.scrollHeight;
                  });
                } else {
                  console.warn('Unexpected line:', line);
                }
              }
              partialData = '';
            }
          } catch (error) {
            this.modalLoading = false;
            console.error('Error:', error);
          }
        },
        getParentDirectory(path) {
          parentDir = path.substring(0, path.lastIndexOf('/'))
          if (parentDir == "")
            parentDir = "/"
          return parentDir;
        },
        scrollToX(top = undefined) {
          if (top == undefined)
            top = document.documentElement.scrollHeight
          window.scrollTo({
            top: top,
            behavior: "smooth"
          });
        },
        getAvailablePlugins(plugins) {
          availablePlugins = {};
          const pluginsFlagsArray = this.plugin_flags.split(',');
          for (const pluginName in plugins) {
            if (!pluginsFlagsArray.includes(`-${pluginName}`)) {
              availablePlugins[pluginName] = plugins[pluginName];
            }
          }
          return availablePlugins;
        },
        toggleAllWeekdays(task) {
          if (task.runweek.length === 7) {
            task.runweek = [];
          } else {
            task.runweek = [1, 2, 3, 4, 5, 6, 7];
          }
        },
        searchSuggestions(index, taskname, deep = 1) {
          if (taskname.length < 2) {
            console.log(`任务名[${taskname}]过短${taskname.length} 不进行搜索`);
            return;
          }
          this.smart_param.isSearching = true;
          this.smart_param.index = index;
          try {
            axios.get('/task_suggestions', {
              params: {
                q: taskname,
                d: deep
              }
            }).then(response => {
              this.smart_param.taskSuggestions = response.data;
              this.smart_param.showSuggestions = true;
            }).catch(error => {
              console.error('Error fetching suggestions:', error);
            }).finally(() => {
              this.smart_param.isSearching = false;
            });
          } catch (e) {
            this.smart_param.taskSuggestions = {
              error: "网络异常"
            };
          }
        },
        selectSuggestion(index, suggestion) {
          this.smart_param.showSuggestions = false;
          this.showShareSelect(index, suggestion.shareurl);
        },
        addMagicRegex() {
          const newKey = `$MAGIC_${Object.keys(this.formData.magic_regex).length + 1}`;
          this.$set(this.formData.magic_regex, newKey, { pattern: '', replace: '' });
        },
        updateMagicRegexKey(oldKey, newKey) {
          if (oldKey !== newKey) {
            if (this.formData.magic_regex[newKey]) {
              alert(`魔法名 [${newKey}] 已存在，请使用其他名称`);
              return;
            }
            this.$set(this.formData.magic_regex, newKey, this.formData.magic_regex[oldKey]);
            this.$delete(this.formData.magic_regex, oldKey);
          }
        },
        removeMagicRegex(key) {
          if (confirm(`确认删除魔法匹配规则 [${key}] 吗？`)) {
            this.$delete(this.formData.magic_regex, key);
          }
        },
        deleteFile(fid, fname, isDir) {
          if (fid != "" && confirm(`确认删除${isDir ? '目录' : '文件'} [${fname}] 吗？`))
            axios.post('/delete_file', {
              fid: fid
            }).then(response => {
              if (response.data.code == 0) {
                this.fileSelect.fileList = this.fileSelect.fileList.filter(item => item.fid != fid);
              } else {
                alert('删除失败：' + response.data.message);
              }
            }).catch(error => {
              console.error('Error /delete_file:', error);
            });
        },
        getSavepathDetail(params = 0) {
          if (params.includes('/')) {
            params = { path: params }
          } else {
            params = { fid: params }
          }
          this.modalLoading = true;
          axios.get('/get_savepath_detail', {
            params: params
          }).then(response => {
            this.fileSelect.fileList = response.data.data.list
            if (response.data.data.paths.length > 0) {
              this.fileSelect.paths = response.data.data.paths
            }
            this.modalLoading = false;
          }).catch(error => {
            console.error('Error /get_savepath_detail:', error);
            this.fileSelect.error = "获取文件夹列表失败";
            this.modalLoading = false;
          });
        },
        showSavepathSelect(index) {
          this.fileSelect.selectShare = false;
          this.fileSelect.selectDir = true;
          this.fileSelect.previewRegex = false;
          this.fileSelect.error = undefined;
          this.fileSelect.fileList = [];
          this.fileSelect.paths = [];
          this.fileSelect.index = index;
          $('#fileSelectModal').modal('toggle');
          this.getSavepathDetail(this.formData.tasklist[index].savepath);
        },
        getShareDetail() {
          this.modalLoading = true;
          axios.post('/get_share_detail', {
            shareurl: this.fileSelect.shareurl,
            stoken: this.fileSelect.stoken,
            regex: {
              pattern: this.formData.tasklist[this.fileSelect.index].pattern,
              replace: this.formData.tasklist[this.fileSelect.index].replace,
              taskname: this.formData.tasklist[this.fileSelect.index].taskname,
              filterwords: this.formData.tasklist[this.fileSelect.index].filterwords,
              magic_regex: this.formData.magic_regex,
              use_sequence_naming: this.formData.tasklist[this.fileSelect.index].use_sequence_naming,
              sequence_naming: this.formData.tasklist[this.fileSelect.index].sequence_naming,
              use_episode_naming: this.formData.tasklist[this.fileSelect.index].use_episode_naming,
              episode_naming: this.formData.tasklist[this.fileSelect.index].episode_naming,
              episode_patterns: this.formData.episode_patterns
            }
          }).then(response => {
            if (response.data.success) {
              this.fileSelect.fileList = response.data.data.list;
              this.fileSelect.paths = response.data.data.paths;
              this.fileSelect.stoken = response.data.data.stoken;
            } else {
              this.fileSelect.error = response.data.data.error
            }
            this.modalLoading = false;
          }).catch(error => {
            console.error('Error getting folders:', error);
            this.fileSelect.error = "获取文件夹列表失败";
            this.modalLoading = false;
          });
        },
        showShareSelect(index, shareurl = null) {
          this.fileSelect.selectShare = true;
          this.fileSelect.fileList = [];
          this.fileSelect.paths = [];
          this.fileSelect.error = undefined;
          if (this.getShareurl(this.fileSelect.shareurl) != this.getShareurl(this.formData.tasklist[index].shareurl)) {
            this.fileSelect.stoken = "";
          }
          this.fileSelect.shareurl = shareurl || this.formData.tasklist[index].shareurl;
          this.fileSelect.index = index;
          $('#fileSelectModal').modal('toggle');
          this.getShareDetail();
        },
        navigateTo(fid, name) {
          path = { fid: fid, name: name }
          if (this.fileSelect.selectShare) {
            this.fileSelect.shareurl = this.getShareurl(this.fileSelect.shareurl, path);
            this.getShareDetail();
          } else {
            if (fid == "0") {
              this.fileSelect.paths = []
            } else {
              index = this.fileSelect.paths.findIndex(item => item.fid === fid);
              if (index !== -1) {
                this.fileSelect.paths = this.fileSelect.paths.slice(0, index + 1)
              } else {
                this.fileSelect.paths.push({ fid: fid, name: name })
              }
            }
            this.getSavepathDetail(fid);
          }
        },
        selectCurrentFolder(addTaskname = false) {
          if (this.fileSelect.selectShare) {
            this.formData.tasklist[this.fileSelect.index].shareurl_ban = undefined;
            this.formData.tasklist[this.fileSelect.index].shareurl = this.fileSelect.shareurl;
          } else {
            this.formData.tasklist[this.fileSelect.index].savepath = "/" + this.fileSelect.paths.map(item => item.name).join("/");
            if (addTaskname) {
              this.formData.tasklist[this.fileSelect.index].savepath += "/" + this.formData.tasklist[this.fileSelect.index].taskname
            }
          }
          $('#fileSelectModal').modal('hide')
        },
        selectStartFid(fid) {
          Vue.set(this.formData.tasklist[this.fileSelect.index], 'startfid', fid);
          $('#fileSelectModal').modal('hide')
        },
        getShareurl(shareurl, path = {}) {
          if (path == {} || path.fid == 0) {
            shareurl = shareurl.match(`.*s/[a-z0-9]+`)[0]
          } else if (shareurl.includes(path.fid)) {
            shareurl = shareurl.match(`.*/${path.fid}[^\/]*`)[0]
          } else if (shareurl.includes('#/list/share')) {
            shareurl = `${shareurl}/${path.fid}-${path.name}`
          } else {
            shareurl = `${shareurl}#/list/share/${path.fid}-${path.name}`
          }
          return shareurl;
        },
        detectNamingMode(task) {
          // 检测是否为顺序命名模式或剧集命名模式
          const sequencePatterns = ['{}', 'E{}', 'EP{}', 'S\\d+E{}', '第{}集', '第{}话', '第{}期'];
          const episodePatterns = ['[]', 'E[]', 'EP[]', 'S\\d+E[]', '第[]集', '第[]话', '第[]期'];
          
          let isSequenceNaming = false;
          let isEpisodeNaming = false;
          
          // 保存当前值以支持撤销操作
          const currentValue = task.pattern;
          
          if (task.pattern) {
            // 检查是否包含任何顺序命名模式
            isSequenceNaming = sequencePatterns.some(pattern => {
              const regexPattern = pattern.replace('{}', '\\{\\}');
              return new RegExp(regexPattern).test(task.pattern);
            });
            
            // 或者用户直接输入包含{}的格式，且替换表达式为空
            if (!isSequenceNaming && task.pattern.includes('{}') && (!task.replace || task.replace === '')) {
              isSequenceNaming = true;
            }
            
            // 检查是否包含任何剧集命名模式
            if (!isSequenceNaming) {
              isEpisodeNaming = episodePatterns.some(pattern => {
                const regexPattern = pattern.replace('[]', '\\[\\]');
                return new RegExp(regexPattern).test(task.pattern);
              });
              
              // 或者用户直接输入包含[]的格式，且替换表达式为空
              if (!isEpisodeNaming && task.pattern.includes('[]') && (!task.replace || task.replace === '')) {
                isEpisodeNaming = true;
              }
            }
          }
          
          // 处理模式切换
          if (isSequenceNaming) {
            // 如果当前不是顺序命名模式，则保存现有的正则表达式
            if (!task.use_sequence_naming) {
              task._pattern_backup = task.pattern;
              task._replace_backup = task.replace;
              task.use_sequence_naming = true;
              task.use_episode_naming = false;
            }
            // 设置序列命名模式
            task.sequence_naming = task.pattern;
          } else if (isEpisodeNaming) {
            // 如果当前不是剧集命名模式，则保存现有的正则表达式
            if (!task.use_episode_naming) {
              task._pattern_backup = task.pattern;
              task._replace_backup = task.replace;
              task.use_episode_naming = true;
              task.use_sequence_naming = false;
            }
            // 设置剧集命名模式
            task.episode_naming = task.pattern;
          } else {
            // 如果当前是顺序命名模式或剧集命名模式，但现在检测不到相应的命名模式
            if (task.use_sequence_naming || task.use_episode_naming) {
              // 如果用户正在删除内容（当前值为空或比上一次更短）
              if (!currentValue || (task._lastPatternValue && currentValue.length < task._lastPatternValue.length)) {
                // 保持当前编辑状态，不切换模式
                if (task.use_sequence_naming) {
                  task.sequence_naming = currentValue;
                } else if (task.use_episode_naming) {
                  task.episode_naming = currentValue;
                }
                
                // 只有当完全删除后才切换回正则模式
                if (!currentValue) {
                  task.use_sequence_naming = false;
                  task.use_episode_naming = false;
                  if (task._pattern_backup) {
                    task.pattern = "";
                    task.replace = task._replace_backup || "";
                  }
                  task.sequence_naming = null;
                  task.episode_naming = null;
                }
              } else if (task._pattern_backup && (!task.pattern.includes('{}') && !task.pattern.includes('[]'))) {
                // 正常切换回正则命名模式（非删除操作）
                task.use_sequence_naming = false;
                task.use_episode_naming = false;
                task.pattern = task._pattern_backup;
                task.replace = task._replace_backup;
                if (task.use_sequence_naming) {
                  task._sequence_backup = task.sequence_naming;
                  task.sequence_naming = null;
                } else if (task.use_episode_naming) {
                  task._episode_backup = task.episode_naming;
                  task.episode_naming = null;
                }
              } else if (!task._pattern_backup && (!task.pattern.includes('{}') && !task.pattern.includes('[]'))) {
                // 没有备份，但需要切换回正则模式
                task.use_sequence_naming = false;
                task.use_episode_naming = false;
                task.sequence_naming = null;
                task.episode_naming = null;
              }
            }
          }
          
          // 保存当前值，用于下次比较
          task._lastPatternValue = currentValue;
          
          // 强制Vue更新视图
          this.$forceUpdate();
        },
        // 增加剧集识别模式
        addEpisodePattern() {
          // 此方法保留但不再使用
        },
        
        // 移除剧集识别模式
        removeEpisodePattern(index) {
          // 此方法保留但不再使用
        }
      }
    });
  </script>
</body>

</html>