用js javascript, vue写了个网络离线应用 --PDJ媒体复读机(外语复读机,英语复读机)

更新:用js javascript, vue写了个网络离线应用--PDJ媒体复读机

项目地址

演示版和使用说明: https://www.bilibili.com/video/BV1h1seeqEav/

单击里面的.srt文件启动媒体复读功能。(欢迎评论提出建议,尤其在安卓手机浏览器里的表现,我没有安卓真机,是用windows安卓模拟器测试的。貌似只有国际版的firefox能在国内安卓手机上不root的情况下调用系统的tts来朗读译文。)

https://note.youdao.com/s/MI81scdr
https://github.com/niubility000/PDJ-Media-Repeater
https://pan.baidu.com/s/1TBRk_TmbZehB9mya6uC9xQ?pwd=xxw8

功能

新增:离线APP模式。在没有网络时也可以用了,关机后重启,断网,关闭服务器等都不影响使用,就像本地app一样。卸载更是简单,直接在首页点击清除缓存即可。(详见后面说明)
新增:缓存媒体文件。
新增:复习模式。

PDJ Media Repeater ( PDJ媒体复读机 )是一个基于.srt字幕文件的 .mp3 和 .mp4 媒体复读机,是一个免费的绿色免安装的软件,可以在windows, linux, mac等系统下运行,甚至可以在路由器上运行。可以作为外语复读机,英语复读机来使用。它是基于免费开源项目 Filebrowser 修改而来。

以windows操作系统为例,直接双击 filebrowser.exe 运行,然后在浏览器中访问127.0.0.1:8080, 192.168.x.x:8080或公网IP:8080即可,默认帐户和密码是admin:admin。其他linux,mac版本启动方法类似。
把媒体文件和同名.srt文件放在filebrowser.exe所在目录,在网页中双击 .srt 文件即可启动 PDJ媒体复读机。

有特点的复读功能包括:可以分别指定每次复读时的速度,可以调用系统自带的tts朗读译文。这些功能对于复读软件很重要,但我这些年使用过数十种复读软件,都没有这些功能(pc端推荐aboboo,手机端推荐轻听英语、可可英语、每日英语听力,不过都可能收费)。作为一个业余编程爱好者和英语使用者,我就自己写了这个免费内的网页版媒体复读机–PDJ Media Repeater,以后可以给孩子学习英语用。网上还没有类似的项目,github里也没有开源好用的复读机,所以写起来还是挺有意思的。

开发过程

离线APP模式最后完成的很简单。本来想用service worker来做的,但只能用在https上,个人使用者没有条件搭建https。所以我就仔细研究cache-control,发现只要在服务器响应头添加 cache-control:max-age=100年的秒数,在客户端js里的非离线APP模式的请求头添加no-cache,在离线APP模式的请求头里什么也不加就能完美实现需求。太简单了。也就是说,服务器发过来的资源都永久不过期(不会影响浏览器缓存大小,因为即使设为1秒过期,过期后浏览器也永久不会删除过期的缓存。),然后在非离线时要求服务器校验资源更新,如果断网,则会页面报错。在离线时永远都先从浏览器缓存里读,缓存里没有再从服务器上读,如果断网,也不会页面崩毁,只是不显示没有的资源。完美!然后再把断网时修改的字幕文件和配置文件存入localstorage里,等联网了再更新到服务器上,这样就会不丢任何信息了,这叫网页应用离线化。做了布局防抖后,用lighthouse测试,性能达到97% (大厂的视频页面性能一般也才30%多),比本地应用使用起来还流畅,因为各大浏览器对资源的优化都很完美

缓存媒体文件功能:本来就是做个在线的英语复读机,发现在外面使用一个月用了300g的手机流量,研究了一下,每次前进后退,都会产生流量,刚播放完的句子复读时还会重新下载,这就只能在有wifi的时候用了。然后我就开始研究媒体缓存,localstorage也就5m,不能用。后来发现indexedDB,这个能存视频和音频啊,我用的第三方的localForage,超简单。完美实现媒体的缓存。我是在加载时先判断localforage里有没有缓存的媒体,如果有,把video 的raw直接设成这个,如果没有,则把raw设成网络地址(它会只下载一个小片段),同时用fetch下载整个媒体文件,存入localforage,下载完成后raw自动切换成这个源。(发现在播放视频时换源,不用用户主动点击都能继续播放,无缝衔接。)这个比用离线缓存速度要快很多,因为离线缓存需要几乎把整个片子都看完才能缓存完整。这个是用fetch在后台主动下载整个视频,速度快很多,而且保证完整性。

old:
因为Filebrowser-PDJ Media Repeater其实就是一个一键启动的http服务器,然后用浏览器即可远程访问和管理本地硬盘上的文件。
所以,PDJ Media Repeater的功能实现是依赖于用户的浏览器的。国内的浏览器五花八门,如果不能完全支持html5标准,就得建议用户使用标准浏览器(chrome, edge, firefox, safari)。

实现起来思路很简单,就是先读取.srt文件,并用video或audio标签引入同一目录下同名的媒体文件。然后把.srt文件断句形成字幕数组,用点击事件、滑动事件、键盘事件切换当前句,显示对应字幕,并播放对应片段的媒体文件。我给用户提供了很多设置选项,叠加起来有16种复读模式。而且可以在网页上全片播放模式下同步显示srt字幕(html5原本默认不支持.srt字幕),并且用键盘左右键直接切换上下句,用键盘向下键重播当前句。

期间遇到的主要问题有:

  1. 移动端(苹果和安卓)必须由用户主动点击屏幕来触发媒体文件的第一次播放,用tts朗读译文也有一样的限制。所以我在点击时,先把媒体文件放1毫秒,或者先把朗读tts放1毫秒,然后再执行需要的任务,这样就能在之后两者都能顺利播放了。
  2. 安卓要求比较严。我没有安卓手机,刚开始做完后在pc端和苹果手机端运行都没问题,后来发现安卓手机浏览器上完全无法运行,然后又在windows下用安卓模拟器一个个函数排查。发现安卓浏览器上要求先play(),再指定起止时间和速度,改完后运行完美了,而且pc端和苹果手机端也没问题。

未解决的问题

  1. auto detect字幕语种的功能太简单。现在的判断方法就是直接判断字幕的第一行的第一个字符是不是英语,如果是,就认为第二行是译文行,直接用tts朗读。如果不是,则用tts朗读第一行。完美的方式应该是判断每行的语种,如果某行的语种和浏览器的默认语种相同,则认为是译文行,并用这种语言来朗读译文。可是js里貌似没有自带的判断字符串语种的方法(好像有一个方法,但需要用户在浏览器安装插件,太麻烦)。自己用正则表达式写吧?也覆盖不全。

  2. 浏览器对speechsynthesis API接口的支持问题。 三大浏览器内核(chrome, firefox, safari)都支持 speechsynthesis API 接口,这个接口可以直接调用系统的tts服务,或在线的tts服务。用pc浏览器和苹果手机浏览器都没问题。
    但国内安卓手机用户会碰到问题。因为安卓的chrome浏览器里的 speechsynthesis API 接口需要用到谷歌框架(我的判断是这样)才能调用安卓手机自带的tts 文字转语音服务。而国内安卓手机都没有谷歌框架,而国内的安卓浏览器(华为小米自带的,微信自带的,qq, uc等浏览器)用的都是谷歌浏览器的内核,所以都不能调用系统的tts服务。国内安卓手机里唯一能朗读译文的浏览器就是原版国际版的firefox(不要用国内修改版),不需要谷歌框架,能直接调用安卓系统的tts服务。
    如果想让国内安卓手机的国产浏览器也能朗读译文,可以在朗读译文时用第三方的在线tts服务,貌似都是收费的,百度的也收费了。而且速度应该会慢,而且没有用系统自带的tts服务稳定。

  3. 国内手机浏览器(安卓或苹果版的qq浏览器,uc浏览器,夸克浏览器等)劫持video, audio标签问题。这个功能本来挺好的,能让用户直接下载任何网页上的视频。但对这个项目就不好了,完全没法控制媒体的播放和暂停,没法设置起止时间和速度了。劫持后,会把视频调到最高层,连设置页面都会遮挡住。貌似网上现在没有能避免劫持的方法,用blob,m3u8,或者画布,试了都挺麻烦,效果还不是很好。我现在的做法是直接判断是否被劫持了,如果发现被劫持了,建议用户更换浏览器。判断方法也有些麻烦,最直接的方法是:判断页面元素最高层的z-index值是否等于video标签的z-index,发现无效,劫持后,页面元素的z-index都没有变。奇了怪了。现在的判断方法是,视频加载成功后,先播放一毫秒的第一句的视频,如果劫持了,这个第一句起止时间设定会无效的,还是会从视频0秒开始播放。

下面是我在filebrowser里新增的repeater.vue组件,用来实现媒体复读功能。大家如果有兴趣,可以帮我改一下,看看有没有更简洁高效的写法。甚至改改css也可以。

主要源码 repeater.vue

(最新版的在这里:https://github.com/niubility000/PDJ-Media-Repeater/blob/main/filebrowser/frontend/src/views/files/Repeater.vue)

<template>
  <div id="repeater">
    <div class="loading delayed" v-if="loading">
      <div class="spinner">
        <div class="bounce1"></div>
        <div class="bounce2"></div>
        <div class="bounce3"></div>
      </div>
    </div>

    <template v-else>
      <header-bar
        v-if="srtSubtitles"
        style="padding: 0.5em"
        :style="{
          height: isMobile && isLandscape ? '3em' : '4em',
          padding: isMobile && isLandscape ? '0 0.5em' : '0.5em 1em 0.5em 1em',
        }"
      >
        <action
          :style="{
            color:
              isSetting ||
              onRevision ||
              isEditSubandNotes ||
              isFavOnPlay ||
              showSubtitleList ||
              showNewWordList ||
              withTrans ||
              showRevision
                ? 'red'
                : 'blue',
          }"
          icon="close"
          :label="$t('buttons.close')"
          @action="close()"
        />
        <title
          style="flex-grow: 1; white-space: nowrap"
          v-if="!isMobile && isFavOnPlay"
        >
          {{ srtSubtitles[this.sentenceIndex - 1].mediaName }}
        </title>
        <title
          style="flex-grow: 1; white-space: nowrap"
          v-if="!isMobile && !isFavOnPlay"
        >
          {{ mediaName }}
        </title>
        <span
          :style="{
            flexGrow: isMobile ? '1' : '0',
          }"
        >
        </span>

        <span
          @click="switchShowList()"
          :style="{
            pointerEvents:
              isSetting || !isSingle || showRevision ? 'none' : 'auto',
          }"
          style="cursor: pointer"
        >
          <span
            style="
              display: inline-block;
              text-align: right;
              border: none;
              padding: 0;
              margin: 0 0.5em 0 0;
              width: 4em;
            "
            :style="{
              color:
                isSetting || !isSingle || showRevision
                  ? 'grey'
                  : showSubtitleList
                  ? 'red'
                  : showNewWordList
                  ? 'black'
                  : 'blue',
            }"
            >{{ sentenceIndex }}/{{ srtSubtitles.length }}</span
          >
        </span>

        <button
          :disabled="
            loading ||
            favList.length == 0 ||
            isSetting ||
            !isSingle ||
            showRevision
          "
          class="action"
          @click="playFavList"
          :title="$t('repeater.playFavoriteList')"
        >
          <i :style="favListStatus" class="material-icons">folder_special</i>
        </button>
        <button
          :disabled="
            loading ||
            !isSingle ||
            showSubtitleList ||
            showNewWordList ||
            showRevision
          "
          class="action"
          @click="onSetting"
          :title="$t('repeater.settings')"
        >
          <i
            :style="{
              color:
                !isSingle || showSubtitleList || showNewWordList || showRevision
                  ? 'grey'
                  : isSetting
                  ? 'red'
                  : 'blue',
            }"
            class="material-icons"
            >settings</i
          >
        </button>

        <button
          v-if="srtSubtitles"
          :disabled="
            loading ||
            isSetting ||
            !isSingle ||
            showSubtitleList ||
            showNewWordList ||
            isFavOnPlay ||
            showRevision
          "
          class="action"
          @click="switchEditSubandNote"
          :title="$t('repeater.editSubandNote')"
        >
          <i
            :style="{
              color:
                isSetting ||
                !isSingle ||
                showSubtitleList ||
                showNewWordList ||
                isFavOnPlay ||
                showRevision
                  ? 'grey'
                  : isEditSubandNotes
                  ? 'red'
                  : 'blue',
            }"
            class="material-icons"
            >edit</i
          >
        </button>

        <button
          :disabled="
            loading ||
            isSetting ||
            showSubtitleList ||
            showNewWordList ||
            isEditSubandNotes ||
            showRevision
          "
          class="action"
          @click="switchSubtitle"
          :title="$t('repeater.switchsubtitleLanguages')"
        >
          <i :style="subSwitch" class="material-icons">closed_caption</i>
        </button>
        <button
          v-if="isSingle"
          :disabled="
            loading ||
            isFavOnPlay ||
            isSetting ||
            showSubtitleList ||
            showNewWordList ||
            isEditSubandNotes ||
            showRevision
          "
          class="action"
          @click="onSingle"
          :title="$t('repeater.singleSentenceRepetitionMode')"
        >
          <i :style="playMode" class="material-icons">repeat_one</i>
        </button>
        <button
          v-if="!isSingle"
          :disabled="loading || showRevision"
          class="action"
          @click="onSingle"
          :title="$t('repeater.regularMode')"
        >
          <i :style="playMode" class="material-icons">repeat</i>
        </button>

        <button
          :disabled="
            loading ||
            isSetting ||
            !isSingle ||
            isFavOnPlay ||
            showSubtitleList ||
            showNewWordList ||
            isEditSubandNotes
          "
          class="action"
          @click="switchRevisePlan"
          :title="$t('repeater.revise')"
        >
          <i
            :style="{
              color:
                loading ||
                isSetting ||
                !isSingle ||
                isFavOnPlay ||
                showSubtitleList ||
                showNewWordList ||
                isEditSubandNotes
                  ? 'grey'
                  : showRevision
                  ? 'red'
                  : onRevision
                  ? 'black'
                  : 'blue',
            }"
            class="material-icons"
            >add_alert</i
          >
        </button>
      </header-bar>
      <div v-if="isSlowInternet" class="showMsg" style="bottom: 2.5em">
        <p style="color: red">
          {{ $t("repeater.slowInternet") }}
        </p>
      </div>

      <div
        v-if="showRevision"
        style="
          background-color: gray;
          color: white;
          z-index: 1010;
          display: flex;
          flex-direction: column;
          position: fixed;
          left: 50%;
          transform: translate(-50%, 0);
          top: 4.2em;
          bottom: 0.2em;
          border-radius: 10px;
          overflow-y: auto;
        "
        :style="{
          width: isMobile ? '100%' : '65%',
        }"
      >
        <p style="padding: 0 1em; color: blue">
          {{ $t("repeater.revise") }}&nbsp;&nbsp;&nbsp;{{ getDateAfterDays(0) }}
        </p>

        <p style="padding: 0 1em">
          {{ $t("repeater.revise1") }}:&nbsp; {{ mediaName }}
        </p>

        <p style="padding: 0 1em">
          {{ $t("repeater.revise4") }}&nbsp;
          <input
            class="input input--repeater"
            style="width: 3.5em; margin: 0; padding: 0"
            type="number"
            min="1"
            v-model.number="indexS"
          />
          &nbsp;to&nbsp;

          <input
            class="input input--repeater"
            style="width: 3.5em; margin: 0; padding: 0"
            type="number"
            min="1"
            v-model.number="indexE"
          />
        </p>

        <p style="padding: 0 1em">
          {{ $t("repeater.revise2") }}&nbsp;
          <input
            class="input input--repeater"
            style="width: 10em; margin: 0; padding: 0"
            type="text"
            v-model="revisePlan"
          />
        </p>

        <p style="padding: 0 1em; margin: 0">
          {{ $t("repeater.revise3") }}
          <button
            class="action"
            style="color: blue"
            @click="addNewRevision"
            :title="$t('repeater.revise3')"
          >
            <i class="material-icons">add_card</i>
          </button>
        </p>

        <div>
          <ul
            style="
              position: relative;
              width: 100%;
              height: 100%;
              padding: 1em;
              overflow-y: auto;
              list-style-type: none;
            "
          >
            <li
              v-for="(item, index) in reviseData"
              :key="index"
              :id="index + 1"
            >
              <hr
                style="border: none; border-top: 1px solid black; height: 0"
              />
              <p style="line-height: 2em">
                <span
                  style="color: blue; cursor: pointer"
                  @click="
                    revisionPlay(
                      item.name,
                      item.startIndex,
                      item.oRawPath,
                      index
                    )
                  "
                >
                  {{ index + 1 }}. {{ item.name }}&nbsp;(
                  {{ item.startIndex }} -- {{ item.endIndex }})&nbsp;:
                </span>
                &nbsp;&nbsp;
                <span v-for="(itm, i) in item.date" :key="i" :id="i + 1">
                  <span
                    style="cursor: pointer"
                    @click="switchDateReviseStatus(index, i)"
                    :style="{
                      color:
                        itm.split('^^')[1].split('**')[0] == '1'
                          ? 'white'
                          : itm.split('**')[1] == '1'
                          ? 'red'
                          : 'black',
                      pointerEvents:
                        itm.split('^^')[1].split('**')[0] == '1' ||
                        itm.split('**')[1] == '0'
                          ? 'none'
                          : 'auto',
                    }"
                  >
                    {{ itm.split("^^")[0] }} &nbsp;&nbsp;
                  </span>
                </span>

                <button
                  class="action"
                  @click="delRevision(index)"
                  :title="$t('repeater.delThisRevision')"
                >
                  <i class="material-icons">delete</i>
                </button>
              </p>
            </li>
          </ul>
        </div>
      </div>

      <div v-if="ShowSwitchSubtitle" class="showMsg" style="bottom: 2.5em">
        <p style="color: yellow">
          {{ indicateSub }}
        </p>
      </div>
      <div
        v-if="showSubtitleList"
        style="
          color: whitesmoke;
          z-index: 1010;
          display: flex;
          position: fixed;
          left: 50%;
          transform: translate(-50%, 0);
          top: 3.2em;
          bottom: 1.2em;
        "
        :style="{
          width: isMobile ? '100%' : '65%',
        }"
      >
        <ul
          style="
            position: relative;
            width: 100%;
            height: 100%;
            padding: 1em;
            border-radius: 10px;
            overflow-y: auto;
            background: grey;
            list-style-type: none;
          "
        >
          <li
            v-for="(subtitle, index) in srtSubtitles"
            :key="index"
            :id="index + 1"
            @click="chooseSentence(index)"
          >
            <p
              style="cursor: pointer"
              :style="{
                color: sentenceIndex == index + 1 ? 'blue' : 'white',
              }"
            >
              {{ index + 1 }}.
              {{ subtitle.content.split("\r\n")[0] }}&nbsp;&nbsp; -
              {{ subtitle.content.split("\r\n")[1] }}
            </p>
            <hr style="border: none; border-top: 1px solid black; height: 0" />
          </li>
        </ul>
      </div>
      <div
        v-if="showNewWordList"
        style="
          background-color: gray;
          color: whitesmoke;
          z-index: 1010;
          display: flex;
          flex-direction: column;
          position: fixed;
          left: 50%;
          transform: translate(-50%, 0);
          top: 4.2em;
          bottom: 0.2em;
          border-radius: 10px;
          overflow-y: auto;
        "
        :style="{
          width: isMobile ? '100%' : '65%',
        }"
      >
        <p v-if="!this.withTrans" style="padding: 0 1em; color: yellow">
          New Words Test
        </p>
        <p v-if="this.withTrans" style="padding: 0 1em; color: yellow">
          New Words List
        </p>
        <ul
          v-if="newWordList.length > 0"
          id="myWordList"
          @click="highLightItem"
          style="
            position: relative;
            width: 100%;
            height: 100%;
            padding: 0 1em;
            overflow-y: auto;
            list-style-type: none;
          "
        >
          <li
            v-for="(newWord, index) in newWordList"
            :key="index"
            :id="index + 1"
            @click="chooseSentence(newWord.num, index)"
          >
            <p
              v-if="!newWord.showTrans && !withTrans"
              style="color: white; cursor: pointer"
            >
              {{ index + 1 }}. {{ newWord.origin }}
            </p>
            <p
              v-if="newWord.showTrans && !withTrans"
              style="color: white; cursor: pointer"
            >
              {{ index + 1 }}.
              {{ newWord.origin }}&nbsp;:&nbsp;&nbsp;&nbsp;&nbsp;{{
                newWord.trans
              }}
            </p>
            <p
              v-if="withTrans"
              style="color: white !important; cursor: pointer"
            >
              {{ index + 1 }}.
              {{ newWord.origin }}&nbsp;:&nbsp;&nbsp;&nbsp;&nbsp;{{
                newWord.trans
              }}
            </p>
            <hr style="border: none; border-top: 1px solid black; height: 0" />
          </li>
        </ul>
        <div
          v-if="newWordList.length == 0"
          style="
            position: relative;
            width: 100%;
            height: 100%;
            padding: 1em;
            border-radius: 10px;
            background: grey;
            top: 1em;
          "
        >
          <p>No New Words defined!</p>
          <p>
            Add a new word or phrase in sentence's note line in Edit Mode with
            format [Original Text:Translation].
          </p>
        </div>
      </div>
      <div id="settingBoxContainer" v-if="srtSubtitles && isSetting">
        <div id="settingBox">
          <p style="text-align: justify; text-align-last: left; color: white">
            {{ $t("repeater.note1") }}
          </p>
          <p style="text-align: justify; text-align-last: left; color: white">
            {{ $t("repeater.note2") }}
            <a
              href="https://ftp.mozilla.org/pub/fenix/releases/125.0/android/fenix-125.0-android-arm64-v8a/fenix-125.0.multi.android-arm64-v8a.apk"
              target="_blank"
              style="color: blue"
              >Firefox Browser</a
            >
          </p>
          <p style="text-align: justify; text-align-last: left; color: white">
            {{ $t("repeater.note3") }}
          </p>
          <p style="color: blue; font-weight: bold; padding-top: 2em">
            {{ $t("repeater.settings") }}
          </p>
          <div style="display: block">
            <span class="subject" :style="{ width: isMobile ? '14em' : '16em' }"
              >{{ $t("repeater.sentencePlaybackTimes") }}
            </span>
            <input
              class="input input--repeater"
              type="number"
              min="0"
              max="1000"
              v-model.number.lazy="repeatTimes"
            />
          </div>
          <div style="display: block">
            <span class="subject" :style="{ width: isMobile ? '14em' : '16em' }"
              >{{ $t("repeater.interval") }}
            </span>
            <input
              class="input input--repeater"
              type="number"
              min="0"
              max="1000"
              v-model.number.lazy="interval"
            />
          </div>
          <div style="display: block">
            <span class="subject" :style="{ width: isMobile ? '14em' : '16em' }"
              >{{ $t("repeater.timestampMove") }}
            </span>
            <input
              class="input input--repeater"
              type="number"
              placeholder="-100"
              step="50"
              v-model.number.lazy="timeStampChangeStart"
            />
          </div>
          <div style="display: block">
            <span class="subject" :style="{ width: isMobile ? '14em' : '16em' }"
              >{{ $t("repeater.timestampMoveEnd") }}
            </span>
            <input
              class="input input--repeater"
              type="number"
              placeholder="100"
              step="50"
              v-model.number.lazy="timeStampChangeEnd"
            />
          </div>
          <div style="display: block">
            <span class="subject" :style="{ width: isMobile ? '14em' : '16em' }"
              >{{ $t("repeater.speedEachTime") }}
            </span>
            <input
              class="input input--repeater"
              type="text"
              placeholder="0.8, 0.5"
              v-model.lazy="currentSpeed"
            />
          </div>
          <div style="display: block">
            <p
              :style="{
                color: isFavOnPlay && isPlayFullFavList ? '#bbbaba' : 'white',
              }"
            >
              <input
                v-if="isFavOnPlay && isPlayFullFavList"
                disabled
                type="checkbox"
              />
              <input
                v-if="!(isFavOnPlay && isPlayFullFavList)"
                type="checkbox"
                v-model="autoPlay"
              />
              {{ $t("repeater.autoPlayCurrentSentence") }}
            </p>
            <p
              :style="{
                color: isEditSubandNotes ? '#bbbaba' : 'white',
              }"
            >
              <input v-if="isEditSubandNotes" disabled type="checkbox" />
              <input
                v-if="!isEditSubandNotes"
                type="checkbox"
                v-model="autoPlayNext"
              />
              {{ $t("repeater.autoSwitchtoNextSentence") }}
            </p>
            <p style="color: white">
              <input type="checkbox" v-model="replayFromStart" />
              {{ $t("repeater.replayFromStart") }}
            </p>
            <hr style="border: none; border-top: 1px solid black; height: 0" />
            <p>
              <span style="color: white">
                <input
                  :disabled="isAutoDetectLang"
                  type="checkbox"
                  v-model="isUtterTransLine"
                />
                {{ $t("repeater.utterSubtitle") }}
              </span>
              <span style="color: white">
                (<input type="checkbox" v-model="isAutoDetectLang" />
                {{ $t("repeater.autoDetect") }})
              </span>
            </p>
            <div
              :style="{ color: isUtterTransLine ? 'white' : '#bbbaba' }"
              :disabled="!isUtterTransLine"
            >
              <p style="margin-bottom: 0">
                <input
                  :disabled="!isUtterTransLine"
                  style="margin-left: 1em"
                  type="radio"
                  value="Yes"
                  v-model="isSystemTTS"
                />
                <span>{{ $t("repeater.systemTTS") }}</span>
              </p>
              <p
                v-if="alertNotSuportSpeechSynthesis"
                style="color: red; margin-left: 2em"
              >
                {{ $t("repeater.speechsynthesisAlert") }}
              </p>
              <p
                style="margin: 0.5em 0 1em 2em"
                :style="{
                  color:
                    isSystemTTS == 'Yes' && isUtterTransLine
                      ? 'white'
                      : '#bbbaba',
                }"
              >
                {{ $t("repeater.SystemTTSnote") }}
              </p>
              <p style="margin-bottom: 0">
                <input
                  :disabled="!isUtterTransLine"
                  style="margin-left: 1em"
                  type="radio"
                  value="No"
                  v-model="isSystemTTS"
                />
                <span>{{ $t("repeater.notSystemTTS") }}</span>
                <button
                  :disabled="isSystemTTS == 'Yes' || !isUtterTransLine"
                  class="action"
                  @click="resetTTSurl"
                  :title="$t('repeater.resetTTSurl')"
                >
                  <i
                    :style="{
                      color:
                        isSystemTTS == 'Yes' || !isUtterTransLine
                          ? '#bbbaba'
                          : 'blue',
                    }"
                    class="material-icons"
                    >settings_backup_restore</i
                  >
                </button>
                <button
                  :disabled="isSystemTTS == 'Yes' || !isUtterTransLine"
                  class="action"
                  @click="testTTSurl"
                  :title="$t('repeater.testTTSurl')"
                >
                  <i
                    :style="{
                      color:
                        isSystemTTS == 'Yes' || !isUtterTransLine
                          ? '#bbbaba'
                          : 'blue',
                    }"
                    class="material-icons"
                    >play_circle_outline</i
                  >
                </button>
              </p>
              <input
                style="
                  margin: 0 0 0.5em 2em;
                  width: calc(100% - 2em);
                  text-align: left;
                "
                :disabled="isSystemTTS == 'Yes' || !isUtterTransLine"
                class="input input--repeater"
                type="text"
                v-model.lazy="TTSurl"
              />
              <p
                style="
                  margin: 0 0 1em 2em;
                  text-align: justify;
                  text-align-last: left;
                  word-wrap: break-word;
                  overflow-wrap: break-word;
                  word-break: break-all;
                "
                :style="{
                  color:
                    isSystemTTS == 'No' && isUtterTransLine
                      ? 'white'
                      : '#bbbaba',
                }"
              >
                {{ $t("repeater.notSystemTTSnote") }}
              </p>
            </div>
            <div style="display: block">
              <span
                :style="{
                  color: isUtterTransLine ? 'white' : '#bbbaba',
                }"
                style="margin-left: 1em"
                class="subject"
              >
                {{ $t("repeater.lineNumOfTrans") }}
              </span>
              <input
                :disabled="!(isUtterTransLine && !isAutoDetectLang)"
                class="input input--repeater"
                type="number"
                min="1"
                max="2"
                v-model.number.lazy="lineNumOfTrans"
              />
            </div>
            <div style="display: block">
              <span
                :style="{
                  color:
                    !isUtterTransLine ||
                    isSystemTTS == 'No' ||
                    !hasSpeechSynthesis
                      ? '#bbbaba'
                      : 'white',
                }"
                style="margin-left: 1em"
                class="subject"
              >
                {{ $t("repeater.langInTransLine") }}
              </span>
              <input
                :disabled="
                  !isUtterTransLine ||
                  isSystemTTS == 'No' ||
                  isAutoDetectLang ||
                  !hasSpeechSynthesis
                "
                class="input input--repeater"
                type="text"
                :placeholder="langInTransLinedefault"
                v-model="langInTransLine"
              />
            </div>
            <div style="display: block">
              <span
                :style="{
                  color:
                    !isUtterTransLine ||
                    isSystemTTS == 'No' ||
                    !hasSpeechSynthesis
                      ? '#bbbaba'
                      : 'white',
                }"
                style="margin-left: 1em"
                class="subject"
              >
                {{
                  $t("repeater.voice", {
                    totalvoices: totalReaders,
                  })
                }}
              </span>
              <input
                :disabled="
                  !isUtterTransLine ||
                  isSystemTTS == 'No' ||
                  !hasSpeechSynthesis
                "
                class="input input--repeater"
                type="number"
                v-model.number.lazy="reader"
                :style="{
                  width: isMobile ? '3em' : '6em',
                }"
              />
              <button
                :disabled="
                  isSystemTTS == 'No' ||
                  !isUtterTransLine ||
                  !hasSpeechSynthesis
                "
                class="action"
                @click="testTTSVoice"
                :title="$t('repeater.testTTSVoice')"
              >
                <i
                  :style="{
                    color:
                      isSystemTTS == 'No' || !isUtterTransLine
                        ? '#bbbaba'
                        : 'blue',
                  }"
                  class="material-icons"
                  >play_circle_outline</i
                >
              </button>
            </div>
            <div style="display: block">
              <span
                :style="{ color: isUtterTransLine ? 'white' : '#bbbaba' }"
                style="margin-left: 1em"
                class="subject"
              >
                {{ $t("repeater.pauseTime") }}
              </span>
              <input
                :disabled="!isUtterTransLine"
                class="input input--repeater"
                type="number"
                min="0"
                max="1000"
                v-model.lazy="pauseTimeTransLine"
              />
            </div>
            <div style="display: block">
              <span
                :style="{
                  color:
                    !isUtterTransLine ||
                    isSystemTTS == 'No' ||
                    !hasSpeechSynthesis
                      ? '#bbbaba'
                      : 'white',
                }"
                style="margin-left: 1em"
                class="subject"
              >
                {{ $t("repeater.speedOfUttering") }}
              </span>
              <input
                :disabled="
                  !isUtterTransLine ||
                  isSystemTTS == 'No' ||
                  !hasSpeechSynthesis
                "
                class="input input--repeater"
                type="text"
                v-model.number.lazy="speedOfUtter"
              />
            </div>
            <p :style="{ color: isUtterTransLine ? 'white' : '#bbbaba' }">
              <input
                :disabled="!isUtterTransLine"
                style="margin-left: 1em"
                type="checkbox"
                v-model="isUtterTransLineFirstly"
              />
              {{ $t("repeater.utterTransFirstly") }}
            </p>
            <p
              :style="{
                color: isUtterTransLine ? 'white' : '#bbbaba',
              }"
              style="
                text-align: justify;
                text-align-last: left;
                margin-left: 1em;
              "
            >
              <input
                :disabled="!isUtterTransLine"
                type="checkbox"
                v-model="isPauseAfterFirstDone"
              />
              {{ $t("repeater.autoPauseAfterFirstDone") }}
            </p>
            <hr style="border: none; border-top: 1px solid black; height: 0" />
            <p
              :style="{
                color: !isFavOnPlay ? 'white' : '#bbbaba',
              }"
              style="text-align: justify; text-align-last: left"
            >
              <input
                :disabled="isFavOnPlay"
                type="checkbox"
                v-model="isPlayFullFavList"
              />
              {{ $t("repeater.playFullFavList") }}
            </p>
            <hr style="border: none; border-top: 1px solid black; height: 0" />
            <p style="color: white; text-align: justify; text-align-last: left">
              <input
                :disabled="allowOffline"
                type="checkbox"
                v-model="allowCache"
              />
              {{ $t("repeater.allowCache") }}
            </p>
            <p style="color: white; text-align: justify; text-align-last: left">
              {{ $t("repeater.cached1") }}
              <input
                style="width: 4em"
                type="number"
                v-model.lazy="maxCacheNum"
              />
            </p>
            <div
              style="color: white; text-align: justify; text-align-last: left"
            >
              <p>
                {{ $t("repeater.savedfiles", { numOfKeys: numOfKeys }) }}
              </p>
              <div v-if="cachedKeys !== ''">
                <ul
                  style="
                    position: relative;
                    width: 100%;
                    height: 100%;
                    padding: 0 1em;
                    border-radius: 10px;
                    background: grey;
                    list-style-type: none;
                  "
                >
                  <li
                    v-for="(name, index) in cachedKeysArr"
                    :key="index"
                    :id="index + 1"
                  >
                    <p
                      style="
                        margin: 0;
                        color: greenyellow;
                        word-break: break-all;
                      "
                    >
                      {{ index + 1 }}.
                      {{ name }}
                    </p>
                  </li>
                </ul>
              </div>

              <p>
                <button :disabled="numOfKeys == 0" @click="cacheCleanUp">
                  {{ $t("repeater.cleanUpCache") }}
                </button>
              </p>

              <p
                style="color: white; text-align: justify; text-align-last: left"
              >
                <input disabled="true" type="checkbox" v-model="allowOffline" />
                {{ $t("repeater.offlineApp") }}
              </p>
            </div>
            <hr style="border: none; border-top: 1px solid black; height: 0" />
          </div>
          <div style="color: white">
            <p style="color: blue; font-weight: bold; padding-top: 2em">
              {{ $t("repeater.instructions") }}
            </p>
            <p style="text-align: justify">
              {{ $t("repeater.instruction1") }}
            </p>
            <p style="text-align: justify">
              {{ $t("repeater.instruction2") }}
            </p>
            <p style="text-align: justify">
              {{ $t("repeater.instruction3") }}
            </p>
            <p style="text-align: justify">
              {{ $t("repeater.instruction4") }}
            </p>
            <p style="text-align: justify">
              {{ $t("repeater.instruction5") }}
            </p>
            <p style="text-align: justify">{{ $t("repeater.instruction6") }}</p>
            <p style="text-align: justify">
              {{ $t("repeater.clickButton") }}
              <i style="color: white" class="material-icons">add_alert</i
              >{{ $t("repeater.instruction14") }}
            </p>
            <p style="text-align: justify">
              {{ $t("repeater.clickButton") }}
              <i style="color: white" class="material-icons">repeat_one</i
              >{{ $t("repeater.instruction7") }}
            </p>
            <p style="text-align: justify">
              {{ $t("repeater.clickButton") }}
              <i style="color: white" class="material-icons">closed_caption</i
              >{{ $t("repeater.instruction8") }}
            </p>
            <p style="text-align: justify">
              {{ $t("repeater.clickButton") }}
              <i style="color: white" class="material-icons">edit</i
              >{{ $t("repeater.instruction13") }}
            </p>
            <p style="text-align: justify">
              {{ $t("repeater.clickButton") }}
              <i style="color: white" class="material-icons">settings</i
              >{{ $t("repeater.instruction9") }}
            </p>
            <p style="text-align: justify">
              {{ $t("repeater.clickButton") }}
              <i style="color: white" class="material-icons">folder_special</i
              >{{ $t("repeater.instruction10") }}
            </p>
            <p style="text-align: justify">
              {{ $t("repeater.clickButton") }}
              <i style="color: white" class="material-icons">grade</i
              >{{ $t("repeater.instruction11") }}
            </p>
            <p style="text-align: justify">
              {{ $t("repeater.clickandInput") }}
              <span style="color: white"
                >{{ sentenceIndex }}/{{ srtSubtitles.length }}&dArr;</span
              >{{ $t("repeater.instruction12") }}
            </p>
            <p style="color: blue; font-weight: bold; padding-top: 2em">
              {{ $t("repeater.learnLangUsingPDJ") }}
            </p>
            <p style="text-align: justify">
              {{ $t("repeater.learnLang1") }}
            </p>
            <p style="text-align: justify">
              {{ $t("repeater.learnLang2") }}
            </p>
            <p style="text-align: justify">
              {{ $t("repeater.learnLang3") }}
            </p>
            <p style="color: blue; font-weight: bold; padding-top: 2em">
              {{ $t("repeater.updatesandComments") }}
            </p>
            <p>
              {{ $t("repeater.github") }}
              <a
                href="https://github.com/niubility000/PDJ-Media-Repeater"
                target="_blank"
                style="color: blue"
              >
                https://github.com/niubility000/PDJ-Media-Repeater</a
              >
            </p>
            <p>
              {{ $t("repeater.youdao") }}
              <a
                href="https://note.youdao.com/s/MI81scdr"
                target="_blank"
                style="color: blue"
              >
                https://note.youdao.com/s/MI81scdr</a
              >
            </p>
            <p>
              {{ $t("repeater.baiduyun") }}
              <a
                href="https://pan.baidu.com/s/1EMm3C3rcWnqqxutBV9W0Mw?pwd=s0wa"
                target="_blank"
                style="color: blue"
              >
                Baidu NetDisk</a
              >
            </p>
          </div>
        </div>
      </div>
      <div
        class="repeater"
        style="display: flex"
        :style="{ paddingTop: isMobile && isLandscape ? '3em' : '4em' }"
      >
        <video
          v-if="isMediaType == 2 && !browserHiJack"
          @mousedown="startDrag"
          @mouseup="endDrag"
          @touchstart="startTouch"
          @touchend="endTouch"
          style="padding-bottom: 1em; padding-top: 0.2em; object-position: top"
          :style="{
            width: isMobile ? '100%' : 'auto',
            height: isMobile && isLandscape ? '0' : isMobile ? '40%' : '60%',
          }"
          id="myVideo"
          :src="raw"
          :autoplay="autoPlay"
          :controls="!isSingle"
          controlslist="noplaybackrate nodownload noremoteplayback"
          disablePictureInPicture="true"
          @loadedmetadata="readyStatus"
          @error="wrongSrc"
          x5-video-player-type="h5-page"
          webkit-playsinline="true"
          playsinline="true"
          x5-video-orientation="landscape|portrait"
        ></video>

        <p
          v-if="isMediaType > 0 && browserHiJack"
          style="color: red; font-size: 1.2em; padding-top: 4em"
        >
          {{ $t("repeater.warning1") }}
        </p>

        <p
          v-if="!isReadyToPlay && isMediaType > 0 && !browserHiJack"
          class="showMsg"
          style="color: grey; bottom: 1.5em"
        >
          Loading Media...
        </p>
        <span
          @mousedown="startDrag"
          @mouseup="endDrag"
          @touchstart="startTouch"
          @touchend="endTouch"
          v-if="srtSubtitles && isMediaType == 1"
          style="
            width: 100%;
            margin: 0;
            background-color: black;
            top: 0;
            height: 20%;
          "
        >
        </span>
        <div v-if="isMediaType > 0 && srtSubtitles && !isEmpty">
          <button
            v-if="isFav"
            class="action"
            @click="switchIsFav"
            :title="$t('repeater.fav')"
          >
            <i
              style="color: yellow; padding: 0 0 0.5em 0; font-size: 1.2em"
              class="material-icons"
              >star</i
            >
          </button>
          <button
            v-if="!isFav"
            class="action"
            @click="switchIsFav"
            :title="$t('repeater.fav')"
          >
            <i
              style="color: yellow; padding: 0 0 0.5em 0; font-size: 1.2em"
              class="material-icons"
              >star_outline</i
            >
          </button>
        </div>
        <span
          v-html="subtitleContent"
          id="subArea"
          @mousedown="startDragS"
          @mouseup="endDragS"
          @touchstart="startTouchS"
          @touchend="endTouchS"
          @dblclick="dblClick"
          v-if="isMediaType > 0 && srtSubtitles && !isEditSubandNotes"
          style="
            color: yellow;
            overflow-wrap: break-word;
            width: 100%;
            font-size: 1.5em;
            margin: 0;
          "
          :style="{ top: isMediaType == 1 ? 0 : '4em' }"
        >
        </span>
        <p v-if="isMediaType == 0" style="color: red">
          Can't find media file: {{ reqF.name.replace(".srt", ".mp4/.mp3") }}.
          (Note: .mp3/mp4 is case sensitive, .srt and .mp3/mp4 files should be
          in the same folder). Or the .srt file's format is incorrect, it should
          be encoded using UTF-8.
        </p>
        <p v-if="srtSubtitles == null" style="color: red">
          This .srt file's format is incorrect. It should be encoded using
          UTF-8.
        </p>

        <span
          @mousedown="startDrag"
          @mouseup="endDrag"
          @touchstart="startTouch"
          @touchend="endTouch"
          v-if="isMediaType > 0 && srtSubtitles && !isEditSubandNotes"
          style="
            color: white;
            width: 100%;
            overflow-wrap: break-word;
            font-size: 1em;
            margin: auto;
            left: 0;
            right: 0;
            overflow: auto;
          "
        >
          <div
            v-show="!isEmpty"
            style="
              width: 100%;
              background-color: black;
              color: whitesmoke;
              border: none;
              resize: none;
              text-align: center;
            "
          >
            <p v-if="isShowLine3">
              {{
                !isEmpty
                  ? srtSubtitles[sentenceIndex - 1].content.split("\r\n")[2]
                  : "     "
              }}
            </p>
          </div>
        </span>
        <span
          v-if="srtSubtitles && isEditSubandNotes && !isEmpty"
          style="
            overflow-wrap: break-word;
            width: 100%;
            margin: 0;
            font-size: 1.2em;
            background-color: black;
            padding-top: 0;
          "
        >
          <p style="font-size: 1em; padding: 0; margin: 0 0 1.5em 0">
            <span
              @click="startTimeMinus()"
              :style="{
                pointerEvents: !isSingle ? 'none' : 'auto',
              }"
              style="cursor: pointer; user-select: none"
            >
              <span
                class="headSubject"
                style="
                  text-align: right;
                  border: none;
                  padding: 0;
                  margin: 0;
                  color: white;
                "
                ><font color="yellow" size="5">&#8711;</font
                ><font color="black">-</font></span
              >
            </span>
            &#32;
            <input
              class="input input--repeater"
              type="number"
              v-model.number.lazy="startTimeTemp"
              id="editArea0"
              step="0.001"
              style="font-size: 1em; padding: 0; margin: 0"
              :style="{
                width: isMobile ? '4.5em' : '5.2em',
              }"
            />
            &#32;
            <span
              @click="startTimeAdd()"
              :style="{
                pointerEvents: !isSingle ? 'none' : 'auto',
              }"
              style="cursor: pointer; user-select: none"
            >
              <span
                class="headSubject"
                style="
                  text-align: right;
                  border: none;
                  padding: 0;
                  margin: 0;
                  color: white;
                "
                ><font color="black">-</font
                ><font color="yellow" size="5">&#916;</font></span
              >
            </span>
            ----------
            <span
              @click="endTimeMinus()"
              :style="{
                pointerEvents: !isSingle ? 'none' : 'auto',
              }"
              style="cursor: pointer; user-select: none"
            >
              <span
                class="headSubject"
                style="
                  text-align: right;
                  border: none;
                  padding: 0;
                  margin: 0;
                  color: white;
                "
                ><font color="yellow" size="5">&#8711;</font
                ><font color="black">-</font></span
              >
            </span>
            &#32;
            <input
              class="input input--repeater"
              type="number"
              v-model.number.lazy="endTimeTemp"
              step="0.001"
              id="editArea00"
              style="font-size: 1em; padding: 0; margin: 0"
              :style="{
                width: isMobile ? '4.5em' : '5.2em',
              }"
            />
            &#32;
            <span
              @click="endTimeAdd()"
              :style="{
                pointerEvents: !isSingle ? 'none' : 'auto',
              }"
              style="cursor: pointer; user-select: none"
            >
              <span
                class="headSubject"
                style="
                  text-align: right;
                  border: none;
                  padding: 0;
                  margin: 0;
                  color: white;
                "
                ><font color="black">-</font
                ><font color="yellow" size="5">&#916;</font></span
              >
            </span>
          </p>

          <textarea
            v-if="isShowLine1"
            id="editArea1"
            v-model.lazy="subFirstLine"
            placeholder="...Subtitle's First Line..."
            :rows="rowsNum"
            style="
              width: 100%;
              text-align: center;
              background-color: black;
              color: white;
              border: none;
              resize: none;
              padding: 0;
            "
          ></textarea>
          <textarea
            v-if="isShowLine2"
            id="editArea2"
            v-model.lazy="subSecLine"
            placeholder="...Subtitle's Second Line..."
            :rows="rowsNum"
            style="
              width: 100%;
              text-align: center;
              background-color: black;
              color: white;
              border: none;
              resize: none;
              padding: 0.5em 0;
            "
          ></textarea>
          <textarea
            v-show="!isEmpty && isShowLine3"
            id="editArea3"
            :rows="rowsNum"
            v-model.lazy="note"
            placeholder="...NOTES..., use format [Original Text: Translation] to add a New Word or Phrase."
            style="
              width: 100%;
              font-size: 0.8em;
              background-color: black;
              color: whitesmoke;
              border: none;
              resize: none;
              text-align: center;
              padding: 0;
              white-space: pre-wrap;
            "
          ></textarea>
          <button
            class="action"
            @click="confirmDelete"
            :title="$t('repeater.infoDelete')"
          >
            <i style="color: red; font-size: 1.5em" class="material-icons"
              >delete</i
            >
          </button>

          <button
            :disabled="lastSentence"
            class="action"
            @click="confirmMerge"
            :title="$t('repeater.infoMerge')"
          >
            <i
              style="font-size: 1.5em"
              :style="{
                color: lastSentence ? 'grey' : 'red',
              }"
              class="material-icons"
              >merge</i
            >
          </button>

          <button
            class="action"
            @click="confirmAdd"
            :title="$t('repeater.infoAdd')"
          >
            <i style="color: red; font-size: 1.5em" class="material-icons"
              >call_split</i
            >
          </button>

          <button
            :disabled="loading || historyIndex < 1"
            class="action"
            @click="changeUndo"
            :title="$t('repeater.undo')"
          >
            <i
              :style="{
                color: loading || historyIndex < 1 ? 'grey' : 'red',
              }"
              style="font-size: 1.5em"
              class="material-icons"
              >undo</i
            >
          </button>

          <button
            :disabled="loading || historyIndex >= changeNew.length"
            class="action"
            @click="changeRedo"
            :title="$t('repeater.redo')"
          >
            <i
              :style="{
                color:
                  loading || historyIndex >= changeNew.length ? 'grey' : 'red',
              }"
              style="font-size: 1.5em"
              class="material-icons"
              >redo</i
            >
          </button>
        </span>

        <div
          @mousedown="startDrag"
          @mouseup="endDrag"
          @touchstart="startTouch"
          @touchend="endTouch"
          v-if="srtSubtitles"
          style="width: 100%; flex-grow: 1"
        ></div>
      </div>

      <div v-if="markRevised" class="showMsg" style="bottom: 2.5em">
        <span style="color: white; padding: 0.3em; background-color: black">
          {{ $t("repeater.revise5") }}
        </span>
      </div>

      <div v-if="mediaCached" class="showMsg" style="bottom: 2.5em">
        <span style="color: blue; padding: 0.3em; background-color: grey">
          {{ $t("repeater.cached") }}
        </span>
      </div>

      <div v-if="RUdoAlert" class="showMsg" style="bottom: 1em">
        <span style="color: blue; background-color: grey">
          {{ $t("repeater.RUdoAlert") }}
        </span>
      </div>

      <audio
        style="
          position: fixed;
          bottom: 10%;
          width: 85%;
          left: 0;
          right: 0;
          margin: auto;
        "
        v-if="isMediaType == 1 && !browserHiJack && raw !== ' '"
        id="myAudio"
        :src="raw"
        :controls="!isSingle"
        controlslist="noplaybackrate nodownload"
        @error="wrongSrc"
        :autoplay="autoPlay"
        @loadedmetadata="readyStatus"
      ></audio>

      <div
        v-if="showAddNew"
        :disabled="loading || isSetting || !isSingle"
        style="z-index: 1011; position: fixed; bottom: 2.5em; right: 1em"
      >
        <button
          v-if="!showEditNew"
          class="action"
          @click="addANewWord"
          :title="$t('repeater.addnewWord')"
        >
          <i style="color: white; font-size: 2.5em" class="material-icons"
            >add_circle</i
          >
        </button>

        <div
          v-if="showEditNew"
          style="border-radius: 10px; background: grey; padding: 0.3em"
        >
          <p style="text-align: justify; text-align-last: left; color: white">
            <span style="width: 8em"> New word/phrase: </span>
            <input
              style="padding: 0.2em; width: 9em"
              class="input input--repeater"
              type="text"
              v-model="newWord"
            />
          </p>
          <p style="text-align: justify; color: white">
            <input
              style="width: 15.5em; padding: 0.2em; margin-right: 0.5em"
              class="input input--repeater"
              type="text"
              placeholder="Translation"
              v-model="newTranslation"
            />
            <button class="action" @click="saveWordToSRT">
              <i
                style="color: blue; width: 1em; padding: 0"
                class="material-icons"
                >save</i
              >
            </button>
          </p>
        </div>
      </div>
    </template>
  </div>
</template>

<script>
import Vue from "vue";
import { mapState } from "vuex";
import { files as api } from "@/api";
import url from "@/utils/url";
import HeaderBar from "@/components/header/HeaderBar.vue";
import Action from "@/components/header/Action.vue";
import { setTimeout } from "core-js";
import localforage from "localforage";

export default {
  name: "repeater",
  components: {
    HeaderBar,
    Action,
  },
  data: function () {
    return {
      sentenceIndex: 1,
      startTime: null,
      startX: null,
      startY: null,
      timeDiff: null,
      distanceX: null,
      distanceY: null,
      repeatTimes: 3,
      interval: 3,
      playCount: 0,
      utterInProcess: false,
      playInProcess: false,
      replayFromStart: false,
      timeOutId: null,
      intervalId: null,
      timeOutId1: null,
      autoPlayNext: true,
      autoPlay: true,
      timeStampChangeStart: 0,
      timeStampChangeEnd: 0,
      currentSpeed: "0.8, 0.5",
      listing: null,
      isSetting: false,
      isEmpty: false,
      isSingle: true,
      favList: [],
      isFav: false,
      isFavOnPlay: false,
      isReadyToPlay: false,
      subtitleLang: 1,
      isShowLine1: true,
      isShowLine2: true,
      isShowLine3: false,
      isUtterTransLine: true,
      pauseTimeTransLine: 3,
      speedOfUtter: 1,
      isUtterTransLineFirstly: false,
      langInTransLine: navigator.language || navigator.userLanguage,
      langInTransLinedefault: navigator.language || navigator.userLanguage,
      lineNumOfTrans: 2,
      isAutoDetectLang: true,
      touches: 0,
      isPauseAfterFirstDone: false,
      pauseAfterFirstDone: false,
      browserHiJack: window.sessionStorage.getItem("isBrowserHiJack"),
      alertNotSuportSpeechSynthesis: false,
      resized: false,
      isFirstClick: true,
      hasSpeechSynthesis:
        !!window.speechSynthesis || "speechSynthesis" in window,
      utterThis: null,
      isPlayFullFavList: false,
      audio: null,
      isSystemTTS: "Yes",
      note: "     ",
      confirmType: "",
      showSubtitleList: false,
      showNewWordList: false,
      sessionLength: null,
      isSlowInternet: false,
      isEditSubandNotes: false,
      subFirstLine: "     ",
      startTimeTemp: 0,
      endTimeTemp: 0,
      subSecLine: "     ",
      ShowSwitchSubtitle: false,
      firstMount: true,
      numOfKeys: 0,
      playFromCache: false,
      reader: 0,
      showAddNew: false,
      showEditNew: false,
      newWord: " ",
      newTranslation: "",
      withTrans: false,
      changeOld: [],
      changeNew: [],
      historyIndex: 0,
      tempOldContent: "",
      onRUdo: false,
      onEdit: false,
      showRevision: false,
      revisePlan: "0 1 3 7 15 30 60",
      reviseData: [],
      onRevision: false,
      oReq: null,
      indexS: 1,
      indexE: 10,
      tempSentenceIndex: 1,
      markRevised: false,
      reviseType: 0,
      srtRevisePath: "",
      maxCacheNum: Number(window.localStorage.getItem("max")) || 10,
      cachedKeys: window.localStorage.getItem("cKeys") || "",
      indexOfNewWordList: 0,
      raw: " ",
      mediaCached: false,
      allowCache: Number(window.localStorage.getItem("cacheOff")) !== 1,
      allowOffline: Number(window.localStorage.getItem("isOffline")) == 1,
      isLandscape: this.checkLandscape(),
      tempFavContent: "",
      notSaveFav: false,
      notFromStarttimeTempChg: true,
      fromMerge: false,
      RUdoAlert: false,
      TTSurl:
        "https://dds.dui.ai/runtime/v1/synthesize?voiceId=xijunm&speed=1.1&volume=100&text=",
    };
  },

  computed: {
    ...mapState(["req", "user", "oldReq", "jwt", "loading"]),

    isMobile() {
      return (
        /iPhone|Android/i.test(navigator.userAgent) && window.innerWidth < 736
      );
    },

    favFileName() {
      return "PDJ-user" + this.user.id + ".txt";
    },

    reqF() {
      if (this.onRevision) return this.oReq;
      else return this.req;
    },

    favNotUpload() {
      return this.favFileName + "favNotUpload";
    },

    srtNotUpload() {
      return this.mediaName + "srtNotUpload";
    },

    lastSentence() {
      return this.sentenceIndex == this.srtSubtitles.length;
    },

    favListStatus() {
      if (this.isSetting || !this.isSingle || this.showRevision)
        return { color: "grey" };
      if (!this.isPlayFullFavList) {
        if (this.currentFileFavList.length == 0) {
          return { color: "grey" };
        } else if (this.isFavOnPlay) {
          return { color: "red" };
        } else {
          return { color: "blue" };
        }
      } else {
        if (this.favList.length == 0) {
          return { color: "grey" };
        } else if (this.isFavOnPlay) {
          return { color: "red" };
        } else {
          return { color: "blue" };
        }
      }
    },

    totalReaders() {
      if (this.hasSpeechSynthesis) {
        let voices = window.speechSynthesis.getVoices();
        let formattedLang =
          this.langInTransLine.substring(0, 3) +
          this.langInTransLine.substring(3).toUpperCase();
        return voices.filter(function (voice) {
          return voice.lang.includes(formattedLang);
        }).length;
      } else return 0;
    },

    subSwitch() {
      if (
        this.isSetting ||
        this.showSubtitleList ||
        this.showNewWordList ||
        this.isEditSubandNotes ||
        this.showRevision
      )
        return { color: "grey" };
      if (this.subtitleLang == 1 || this.subtitleLang == 2) {
        return { color: "blue" };
      } else if (this.subtitleLang == 3) {
        return { color: "red" };
      } else if (
        this.subtitleLang == 4 ||
        this.subtitleLang == 5 ||
        this.subtitleLang == 6 ||
        this.subtitleLang == 7
      ) {
        return { color: "green" };
      } else {
        return { color: "black" };
      }
    },

    indicateSub() {
      if (this.subtitleLang == 1) {
        return "1. show Subtitle's First Line and Second Line";
      } else if (this.subtitleLang == 2) {
        return "2. show ALL";
      } else if (this.subtitleLang == 3) {
        return "3. show Note Line only";
      } else if (this.subtitleLang == 4) {
        return "4. show Subtitle's First Line only";
      } else if (this.subtitleLang == 5) {
        return "5. show Subtitle's Second Line only";
      } else if (this.subtitleLang == 6) {
        return "6. show Subtitle's First Line and Note Line";
      } else if (this.subtitleLang == 7) {
        return "7. show Subtitle's Second Line and Note Line";
      } else {
        return "8. show NONE";
      }
    },

    playMode() {
      if (
        this.loading ||
        this.isSetting ||
        this.showSubtitleList ||
        this.showNewWordList ||
        this.isEditSubandNotes ||
        this.showRevision
      ) {
        return { color: "grey" };
      } else if (this.playFromCache) {
        return { color: "red" };
      } else {
        return { color: "blue" };
      }
    },

    rowsNum() {
      return this.isMobile && !this.isLandscape ? 2 : 1;
    },

    srtSubtitles() {
      if (!this.isFavOnPlay) {
        var formatContent = this.reqF.content;
        formatContent = this.formatAll(formatContent);

        var subtitles = [];
        var textSubtitles = formatContent.split("\n\n");
        for (var i = 0; i < textSubtitles.length; ++i) {
          var textSubtitle = textSubtitles[i].split("\n");
          if (textSubtitle.length >= 2) {
            var sn = i + 1;
            var startTimeUnformat = textSubtitle[1].split(" --> ")[0];
            var startHH = startTimeUnformat.split(":")[0];
            var startMM = startTimeUnformat.split(":")[1];
            var startSS = startTimeUnformat.split(":")[2];
            var startMS = startTimeUnformat.split(",")[1];
            var startTime =
              parseFloat(startHH) * 3600 +
              parseFloat(startMM) * 60 +
              parseFloat(startSS) +
              (parseFloat(startMS) + this.timeStampChangeStart) / 1000;
            var endTimeUnformat = textSubtitle[1].split(" --> ")[1];
            if (!endTimeUnformat) return null;
            var endHH = endTimeUnformat.split(":")[0];
            var endMM = endTimeUnformat.split(":")[1];
            var endSS = endTimeUnformat.split(":")[2];
            var endMS = endTimeUnformat.split(",")[1];
            var endTime =
              parseFloat(endHH) * 3600 +
              parseFloat(endMM) * 60 +
              parseFloat(endSS) +
              (parseFloat(endMS) + this.timeStampChangeEnd - 1) / 1000;
            var content = "";
            if (textSubtitle.length >= 3) {
              content = textSubtitle[2]
                .replace(/^\s\s*/, "")
                .replace(/\s\s*$/, "");
              if (content == "") content = " ";
              if (textSubtitle.length > 3) {
                for (var j = 3; j < textSubtitle.length; j++) {
                  content +=
                    "\r\n" +
                    textSubtitle[j].replace(/^\s\s*/, "").replace(/\s\s*$/, "");
                }
              }
            }
            var subtitle = {
              sn: sn,
              timeStamp: textSubtitle[1],
              startTime: startTime,
              endTime: endTime,
              content: content,
            };
            subtitles.push(subtitle);
          }
        }
        return subtitles;
      } else if (this.isPlayFullFavList) {
        return this.favList;
      } else {
        return this.currentFileFavList;
      }
    },

    cachedKeysArr() {
      let temp = this.cachedKeys.replace(";;", "");
      return temp.split(";;");
    },

    newWordList() {
      var wordList = [];
      var origin = "";
      var trans = "";
      var sIndex = this.sentenceIndex - 1;
      for (var i = 0; i < this.srtSubtitles.length; ++i) {
        if (
          this.srtSubtitles[i].content.split("\r\n")[2] &&
          this.srtSubtitles[i].content.split("\r\n")[2].includes("[")
        ) {
          for (
            var j = 1;
            j < this.srtSubtitles[i].content.split("\r\n")[2].split("[").length;
            ++j
          ) {
            origin = this.srtSubtitles[i].content
              .split("\r\n")[2]
              .split("[")
              [j].split(":")[0];
            trans = this.srtSubtitles[i].content
              .split("\r\n")[2]
              .split("[")
              [j].split(":")[1]
              .split("]")[0];

            if (origin !== "") {
              sIndex = parseInt(this.srtSubtitles[i].sn) - 1;
              var newWordItem = {
                num: sIndex,
                showTrans: false,
                origin: origin,
                trans: trans,
              };
              wordList.push(newWordItem);
            }
          }
        }
      }
      return wordList;
    },

    isMediaType() {
      if (this.isFavOnPlay && this.isPlayFullFavList) {
        if (
          this.srtSubtitles[this.sentenceIndex - 1].mediaName.includes(".mp4")
        ) {
          return 2;
        } else return 1;
      } else {
        if (this.onRevision) {
          return this.reviseType;
        }
        if (this.listing && this.reqF.name) {
          for (var i = 0; i < this.listing.length; ++i) {
            if (
              this.listing[i].name == this.reqF.name.replace(".srt", ".mp4")
            ) {
              return 2;
            } else if (
              this.listing[i].name == this.reqF.name.replace(".srt", ".mp3")
            ) {
              return 1;
            }
          }
          return 0;
        } else {
          return -1;
        }
      }
    },

    currentMedia() {
      if (this.isMediaType == 1) {
        return document.getElementById("myAudio");
      } else if (this.isMediaType == 2) {
        return document.getElementById("myVideo");
      } else return null;
    },

    currentFileFavList() {
      let currentMediaName = this.reqF.name;
      return this.favList.filter(function (item) {
        return item.rawPath == currentMediaName;
      });
    },

    mediaName() {
      if (this.isFavOnPlay && this.isPlayFullFavList) {
        return this.srtSubtitles[this.sentenceIndex - 1].mediaName;
      } else {
        if (this.isMediaType == 1) {
          return this.reqF.name.replace(".srt", ".mp3");
        } else if (this.isMediaType == 2) {
          return this.reqF.name.replace(".srt", ".mp4");
        } else {
          return "";
        }
      }
    },

    subtitleContent() {
      if (this.srtSubtitles[this.sentenceIndex - 1].content) {
        var contentLine1 =
          !this.isEmpty &&
          this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[0] !==
            undefined
            ? this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[0]
            : " ";
        var contentLine2 =
          !this.isEmpty &&
          this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[1] !==
            undefined
            ? this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[1]
            : " ";
        var contentAll = "    ";
        if (this.isShowLine1 && this.isShowLine2) {
          contentAll =
            "<p style='margin-top: 0px'>" +
            contentLine1 +
            "</p><p>" +
            contentLine2 +
            "</p>";
        } else if (this.isShowLine1 && !this.isShowLine2) {
          contentAll = "<p style='margin-top: 0px'>" + contentLine1 + "</p>";
        } else if (!this.isShowLine1 && this.isShowLine2) {
          contentAll = "<p style='margin-top: 0px'>" + contentLine2 + "</p>";
        } else {
          contentAll = " ";
        }
        var highLightWord = "";
        if (
          this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[2] &&
          this.srtSubtitles[this.sentenceIndex - 1].content
            .split("\r\n")[2]
            .includes("[")
        ) {
          for (
            var i = 1;
            i <
            this.srtSubtitles[this.sentenceIndex - 1].content
              .split("\r\n")[2]
              .split("[").length;
            ++i
          ) {
            if (
              this.srtSubtitles[this.sentenceIndex - 1].content
                .split("\r\n")[2]
                .split("[")
                [i].includes(":")
            ) {
              highLightWord = this.srtSubtitles[this.sentenceIndex - 1].content
                .split("\r\n")[2]
                .split("[")
                [i].split(":")[0];
            } else {
              highLightWord = this.srtSubtitles[this.sentenceIndex - 1].content
                .split("\r\n")[2]
                .split("[")
                [i].split("]")[0];
            }
            var reg = new RegExp("(" + highLightWord + ")", "g");
            if (highLightWord !== "" && highLightWord !== " ")
              contentAll = contentAll.replace(reg, "<font color=red>$1</font>");
          }
        }
        return contentAll;
      } else return "";
    },

    isEnglishLine1() {
      let str = this.srtSubtitles[this.sentenceIndex - 1].content
        .split("\r\n")[0]
        .replace(/^\s\s*/, "")
        .replace(/\s\s*$/, "");
      return /^[a-zA-Z]/.test(str);
    },

    isEnglishLine2() {
      if (
        !this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[1] ||
        this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[1] ==
          " "
      )
        return false;
      else {
        let str = this.srtSubtitles[this.sentenceIndex - 1].content
          .split("\r\n")[1]
          .replace(/^\s\s*/, "")
          .replace(/\s\s*$/, "");
        return /^[a-zA-Z]/.test(str);
      }
    },

    isTouchDevice() {
      return (
        "ontouchstart" in window ||
        navigator.maxTouchPoints > 0 ||
        navigator.msMaxTouchPoints > 0
      );
    },

    canUtter() {
      let hasContent =
        this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[
          this.lineNumOfTrans - 1
        ];
      return hasContent !== undefined && hasContent !== " ";
    },
    isIphone() {
      return /iPhone/i.test(navigator.userAgent);
    },
  },

  watch: {
    $route: function () {
      this.updatePreview();
    },

    maxCacheNum: function () {
      if (
        window.localStorage.getItem("max") &&
        this.maxCacheNum < Number(window.localStorage.getItem("max"))
      )
        this.cacheCleanUp();
      window.localStorage.setItem("max", this.maxCacheNum);
    },

    mediaName: function () {
      if (this.isMediaType !== -1) {
        if (
          !window.localStorage.getItem(this.mediaName) &&
          !(this.isFavOnPlay && this.isPlayFullFavList)
        )
          window.localStorage.setItem(this.mediaName, this.reqF.content);
        this.getCacheMedia();
      }
    },

    startTimeTemp: function () {
      this.notFromStarttimeTempChg = false;
      this.onEdit = true;
      this.saveSub1();
    },

    endTimeTemp: function () {
      this.onEdit = true;
      this.saveSub2();
    },

    subFirstLine: function () {
      this.onEdit = true;
      this.saveSub();
    },

    subSecLine: function () {
      this.onEdit = true;
      this.saveSub();
    },

    note: function () {
      this.onEdit = true;
      this.saveSub();
    },

    sentenceIndex: function () {
      if (this.isEditSubandNotes) {
        this.onRUdo = true;
        setTimeout(() => {
          this.onRUdo = false;
        }, 1000);
      }
      if (this.isEditSubandNotes) {
        this.startTimeTemp =
          this.srtSubtitles[this.sentenceIndex - 1].startTime;
        this.endTimeTemp = this.srtSubtitles[this.sentenceIndex - 1].endTime;
        this.subFirstLine =
          this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[0];
        this.subSecLine =
          this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[1];
        this.note =
          this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[2];
      }
      this.calcFav();
      if (this.showSubtitleList) {
        document
          .getElementById(this.sentenceIndex)
          .scrollIntoView({ block: "center", behavior: "smooth" });
      }
    },

    isSetting: function () {
      if (
        !this.isSetting &&
        this.isSystemTTS == "Yes" &&
        !this.hasSpeechSynthesis
      ) {
        this.isSystemTTS = "No";
      }
    },

    repeatTimes: function () {
      if (this.repeatTimes < 0) this.repeatTimes = 0;
      this.repeatTimes = Math.floor(this.repeatTimes);
      this.save();
    },

    reviseData: function () {
      this.save();
    },

    revisePlan: function () {
      this.save();
    },

    replayFromStart: function () {
      this.save();
    },

    interval: function () {
      if (this.interval < 0) this.interval = 0;
      this.interval = Math.floor(this.interval);
      this.save();
    },

    autoPlayNext: function () {
      this.save();
    },

    currentSpeed: function () {
      if (this.currentSpeed == "") this.currentSpeed = "0.8, 0.5";
      this.save();
    },

    timeStampChangeStart: function () {
      this.timeStampChangeStart = Math.floor(this.timeStampChangeStart);
      this.save();
    },

    timeStampChangeEnd: function () {
      this.timeStampChangeEnd = Math.floor(this.timeStampChangeEnd);
      this.save();
    },

    subtitleLang: function () {
      this.save();
    },

    isUtterTransLine: function () {
      this.save();
    },

    isSystemTTS: function () {
      if (this.isSystemTTS == "Yes" && !this.hasSpeechSynthesis) {
        this.alertNotSuportSpeechSynthesis = true;
      }
      if (this.isSystemTTS == "No" && !this.hasSpeechSynthesis) {
        this.alertNotSuportSpeechSynthesis = false;
      }
      this.save();
    },

    TTSurl: function () {
      this.save();
    },

    pauseTimeTransLine: function () {
      if (this.pauseTimeTransLine < 0) this.pauseTimeTransLine = 0;
      this.pauseTimeTransLine = Math.floor(this.pauseTimeTransLine);
      this.save();
    },

    speedOfUtter: function () {
      if (this.speedOfUtter < 0.1) this.speedOfUtter = 0.1;
      this.save();
    },

    isUtterTransLineFirstly: function () {
      this.save();
    },

    langInTransLine: function () {
      this.save();
    },

    lineNumOfTrans: function () {
      this.lineNumOfTrans = Math.floor(this.lineNumOfTrans);
      if (this.lineNumOfTrans < 1) this.lineNumOfTrans = 1;
      if (this.lineNumOfTrans > 2) this.lineNumOfTrans = 2;
      this.save();
    },

    isAutoDetectLang: function () {
      if (this.isAutoDetectLang) {
        this.langInTransLine = navigator.language || navigator.userLanguage;
        this.autoDetectLangInTrans();
      }

      this.save();
    },

    isPauseAfterFirstDone: function () {
      this.save();
    },

    autoPlay: function () {
      this.save();
    },

    isPlayFullFavList: function () {
      this.save();
    },

    allowCache() {
      if (this.allowCache) window.localStorage.setItem("cacheOff", 0);
      else {
        window.localStorage.setItem("cacheOff", 1);
      }
      setTimeout(() => {
        location.reload();
      }, 300);
    },

    reader() {
      if (this.hasSpeechSynthesis) {
        if (this.reader < 1) this.reader = 1;
        this.reader = Math.floor(this.reader);
        window.localStorage.setItem("reader", this.reader);
      } else this.reader = 0;
    },

    raw: function () {
      this.isReadyToPlay = false;
      this.playCount = 0;
      this.firstMount = false;
      this.isFirstClick = true;
      if (this.isFavOnPlay && this.isAutoDetectLang)
        this.autoDetectLangInTrans();

      if (!this.isFavOnPlay && !this.onRevision && !this.showRevision) {
        if (
          (this.allowOffline ||
            window.localStorage.getItem(this.srtNotUpload)) &&
          window.localStorage.getItem(this.mediaName)
        ) {
          this.reqF.content = window.localStorage.getItem(this.mediaName);
          if (window.localStorage.getItem(this.srtNotUpload)) {
            this.onRUdo = true;
            this.saveSubNow();
          }
        } else if (
          !this.allowOffline &&
          !window.localStorage.getItem(this.srtNotUpload)
        ) {
          window.localStorage.setItem(this.mediaName, this.reqF.content);
        }
      }
    },
  },

  async mounted() {
    window.localStorage.setItem("cachedOther", 1);
    window.addEventListener("keydown", this.key);
    window.addEventListener("resize", this.handleResize);
    this.listing = this.oldReq.items;
    this.updatePreview();
    this.initUtter();
    if (this.reqF.content == undefined) {
      alert(
        "Can't read content of .srt. The .srt file should be encoded using UTF-8!"
      );
    }
    this.reqF.content = this.formatAll(this.reqF.content);
    if (this.allowOffline) this.allowCache = true;
    this.getReader();
  },

  beforeDestroy() {
    window.removeEventListener("keydown", this.key);
    window.removeEventListener("resize", this.handleResize);
    this.cleanUp1();
  },

  methods: {
    async readyStatus() {
      if (this.currentMedia) this.currentMedia.pause();
      var PDJcontent = "";
      var PDJserverContent = null;

      if (
        (this.allowOffline ||
          window.localStorage.getItem(this.favNotUpload) ||
          (this.isFavOnPlay && this.isPlayFullFavList) ||
          this.showRevision) &&
        window.localStorage.getItem(this.favFileName)
      ) {
        PDJcontent = window.localStorage.getItem(this.favFileName);
      } else {
        try {
          PDJserverContent = await api.fetch("/files/" + this.favFileName);
          PDJcontent = PDJserverContent.content;
          this.serverFav = PDJcontent;
        } catch (e) {
          this.isReadyToPlay = true;
          this.confirmType = "fetch";
          this.showConfirm();
        }
      }

      this.notSaveFav = true;
      setTimeout(() => {
        this.notSaveFav = false;
      }, 500);

      if (PDJcontent !== "") {
        this.repeatTimes = Number(JSON.parse(PDJcontent.split("::")[1]));
        this.interval = Number(JSON.parse(PDJcontent.split("::")[2]));
        this.autoPlayNext = JSON.parse(PDJcontent.split("::")[3]);
        this.timeStampChangeStart = Number(
          JSON.parse(PDJcontent.split("::")[4])
        );
        this.timeStampChangeEnd = Number(
          JSON.parse(PDJcontent.split("::")[20])
        );
        this.currentSpeed = JSON.parse(PDJcontent.split("::")[5]);
        this.subtitleLang = JSON.parse(PDJcontent.split("::")[6]);
        this.switchSubtitleMini();
        this.pauseTimeTransLine = Number(JSON.parse(PDJcontent.split("::")[8]));
        this.speedOfUtter = Number(JSON.parse(PDJcontent.split("::")[9]));
        this.isUtterTransLineFirstly = JSON.parse(PDJcontent.split("::")[10]);
        this.isPauseAfterFirstDone = JSON.parse(PDJcontent.split("::")[14]);
        this.autoPlay = JSON.parse(PDJcontent.split("::")[15]);
        this.isSystemTTS = JSON.parse(PDJcontent.split("::")[16]);
        this.TTSurl = JSON.parse(PDJcontent.split("::")[17]);
        this.replayFromStart = JSON.parse(PDJcontent.split("::")[18]);
        this.isPlayFullFavList = JSON.parse(PDJcontent.split("::")[19]);
        this.reviseData = JSON.parse(PDJcontent.split("::")[21]);
        this.revisePlan = JSON.parse(PDJcontent.split("::")[22]);
        this.isAutoDetectLang = JSON.parse(PDJcontent.split("::")[13]);
        if (!this.isAutoDetectLang) {
          this.isUtterTransLine = JSON.parse(PDJcontent.split("::")[7]);
          this.langInTransLine = JSON.parse(PDJcontent.split("::")[11]);
          this.lineNumOfTrans = Number(JSON.parse(PDJcontent.split("::")[12]));
        } else {
          this.autoDetectLangInTrans();
          this.langInTransLine = navigator.language || navigator.userLanguage;
        }
        this.favList = JSON.parse(PDJcontent.split("Subtitle:")[1]);
        this.calcFav();
        if (!this.hasSpeechSynthesis) {
          this.isSystemTTS = "No";
        }
        this.isReadyToPlay = true;
      }
      if (
        window.localStorage.getItem(this.favNotUpload) ||
        !window.localStorage.getItem(this.favFileName)
      ) {
        this.notSaveFav = false;
        this.save();
      }
    },

    checkLandscape() {
      return window.matchMedia("(orientation: landscape)").matches;
    },

    cacheMedia() {
      let keyName = this.mediaName;
      if (window.localStorage.getItem("cKeys") == null)
        window.localStorage.setItem("cKeys", this.cachedKeys);
      else this.cachedKeys = window.localStorage.getItem("cKeys");
      var vmcachedKeys = this.cachedKeys;
      var vmmax = this.maxCacheNum + 1;
      fetch(this.raw)
        .then((response) => response.blob())
        .then((blob) => {
          let vmm = this;
          localforage.setItem(keyName, blob, function () {
            vmcachedKeys = vmcachedKeys + ";;" + keyName;
            window.localStorage.setItem("cKeys", vmcachedKeys);
            var ck = vmcachedKeys.split(";;");
            if (ck.length > vmmax) {
              var keyName1 = ck[1];
              localforage.removeItem(keyName1, function () {
                console.log("we just removed: " + keyName1);
              });
              vmcachedKeys = vmcachedKeys.replace(";;" + ck[1], "");
              window.localStorage.setItem("cKeys", vmcachedKeys);
            }
            vmm.cachedKeys = vmcachedKeys;
            setTimeout(() => {
              if (keyName !== vmm.mediaName) {
                return; // may multiple running at the same time.
              }
              if (!vmm.isSingle && !vmm.currentMedia.paused) {
                window.localStorage.setItem(
                  "onFullPlaying",
                  vmm.currentMedia.currentTime
                );
              }
              localforage
                .getItem(keyName)
                .then(function (value) {
                  vmm.raw = URL.createObjectURL(value);
                  vmm.playFromCache = true;
                  vmm.mediaCached = true;
                })
                .catch(function () {});
            }, 200);

            setTimeout(() => {
              vmm.mediaCached = false;
              if (window.localStorage.getItem("onFullPlaying")) {
                vmm.regularPlay();
                vmm.currentMedia.currentTime = Number(
                  window.localStorage.getItem("onFullPlaying")
                );
                window.localStorage.removeItem("onFullPlaying");
              }
            }, 2000);
          });
        })
        .catch((error) => {
          console.error("Error fetching or converting URL:", error);
        });
    },

    wrongSrc() {
      if (this.isFavOnPlay && this.isPlayFullFavList) {
        this.confirmType = "wrongSrc";
        this.showConfirm();
      }
    },

    getDateAfterDays(days) {
      const date = new Date();
      date.setDate(date.getDate() + days);
      return date.toISOString().split("T")[0];
    },

    compareDates(date) {
      const d1 = new Date();
      const d2 = new Date(date);
      if (d1.getTime() < d2.getTime()) {
        return 1;
      } else {
        return 0;
      }
    },

    addNewRevision() {
      if (this.indexS >= this.indexE) {
        this.indexE = this.indexS + 1;
      }
      let reviseDate = [];
      let reviseTemp1 = this.revisePlan.split(" ");
      let srtUrl = api.getDownloadURL(this.reqF, true);
      let reviseTemp2 = "";
      for (var i = 0; i < reviseTemp1.length; ++i) {
        if (i == 0 && parseInt(reviseTemp1[0]) == 0) {
          reviseTemp2 =
            this.getDateAfterDays(parseInt(reviseTemp1[i])) +
            "^^" +
            "0" +
            "**" +
            "1";
        } else {
          reviseTemp2 =
            this.getDateAfterDays(parseInt(reviseTemp1[i])) +
            "^^" +
            "1" +
            "**" +
            "1";
        }
        reviseDate.push(reviseTemp2);
      }
      var reviseItem = {
        oRawPath: srtUrl.split("?")[0].split("/raw/")[1],
        name: this.mediaName,
        startIndex: this.indexS,
        endIndex: this.indexE,
        date: reviseDate,
      };
      for (var ii = 0; ii < this.reviseData.length; ++ii) {
        if (
          srtUrl.split("?")[0].split("/raw/")[1] ==
            this.reviseData[ii].oRawPath &&
          this.indexS == this.reviseData[ii].startIndex &&
          this.indexE == this.reviseData[ii].endIndex
        ) {
          let iii = ii + 1;
          alert("Already existed in Review Plan, Item " + iii + ".");
          return;
        }
      }
      this.reviseData.unshift(reviseItem);
    },

    calcFav() {
      this.isFav = false;
      if (this.isFavOnPlay) this.isFav = true;
      else if (this.currentFileFavList) {
        for (var i = 0; i < this.currentFileFavList.length; ++i) {
          if (
            this.currentFileFavList[i].startTime ==
            this.srtSubtitles[this.sentenceIndex - 1].startTime
          ) {
            this.isFav = true;
          }
        }
      }
    },

    async revisionPlay(name, startIndex, oRawPath, index) {
      if (name.endsWith(".mp3")) this.reviseType = 1;
      else this.reviseType = 2;
      this.srtRevisePath = "/files/" + oRawPath;
      try {
        this.oReq = await api.fetch(this.srtRevisePath);
        var tempMediaName = name + "srtNotUpload";
        if (
          (this.allowOffline || window.localStorage.getItem(tempMediaName)) &&
          window.localStorage.getItem(name)
        ) {
          this.oReq.content = window.localStorage.getItem(name);
        } else if (
          !this.allowOffline &&
          !window.localStorage.getItem(tempMediaName)
        ) {
          window.localStorage.setItem(name, this.oReq.content);
        }
        this.onRevision = true;
        this.serverFav = this.tempSentenceIndex = this.sentenceIndex;
        this.sentenceIndex = startIndex;
        this.showRevision = false;
      } catch (e) {
        this.confirmType = "fetchRevision";
        this.showConfirm(index);
      }
    },

    convertToHMS(milliseconds) {
      milliseconds = Math.round(milliseconds);
      var hours = Math.floor(milliseconds / 3600000);
      milliseconds = milliseconds % 3600000;
      var minutes = Math.floor(milliseconds / 60000);
      milliseconds = milliseconds % 60000;
      var seconds = Math.floor(milliseconds / 1000);
      milliseconds = milliseconds % 1000;

      return {
        hours: ("0" + hours).slice(-2),
        minutes: ("0" + minutes).slice(-2),
        seconds: ("0" + seconds).slice(-2),
        milliseconds: ("0" + milliseconds).slice(-3),
      };
    },

    calcRaw() {
      let srtUrl = api.getDownloadURL(this.reqF, true);
      if (this.isFavOnPlay && this.isPlayFullFavList) {
        this.raw =
          srtUrl.split("/raw/")[0] +
          "/raw/" +
          this.srtSubtitles[this.sentenceIndex - 1].originalRawPath.split(
            "?"
          )[0] +
          "?" +
          srtUrl.split("?")[1];
      } else {
        if (srtUrl && this.isMediaType == 1) {
          this.raw = srtUrl.replace(".srt", ".mp3");
        } else if (srtUrl && this.isMediaType == 2) {
          this.raw = srtUrl.replace(".srt", ".mp4");
        } else this.raw = " ";
      }
    },

    getCacheMedia() {
      setTimeout(() => {
        if (!this.allowCache) {
          this.calcRaw();
          return;
        }
        let keyName = this.mediaName;
        if (!this.cachedKeys.includes(";;" + keyName)) {
          this.calcRaw();
          this.playFromCache = false;
          this.cacheMedia();
          return;
        }
        let vm = this;
        localforage
          .getItem(keyName)
          .then(function (value) {
            vm.raw = URL.createObjectURL(value);
            vm.playFromCache = true;
          })
          .catch(function () {
            alert(
              "Something Wrong with your Browser Database for Cached Media. Please clear Up the Cached Media and try again!"
            );
          });
      }, 50);
    },

    cachedNumber() {
      var ck = window.localStorage.getItem("cKeys");
      if (ck) this.numOfKeys = ck.split(";;").length - 1;
      else this.numOfKeys = 0;
    },

    addANewWord() {
      this.showEditNew = true;
    },

    saveWordToSRT() {
      let newphrase = "[" + this.newWord + ":" + this.newTranslation + "]; ";
      this.cleanUp1();
      this.cleanUp2();
      this.startTimeTemp = this.srtSubtitles[this.sentenceIndex - 1].startTime;
      this.endTimeTemp = this.srtSubtitles[this.sentenceIndex - 1].endTime;
      this.subFirstLine =
        this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[0];
      this.subSecLine =
        this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[1];
      this.note =
        this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[2];
      if (this.note == undefined) this.note = "";
      this.note = this.note + newphrase;
      this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[2] =
        this.note;
      this.onEdit = true;
      this.saveSub();
    },

    getReader() {
      if (!this.hasSpeechSynthesis) this.reader = 0;
      if (window.localStorage.getItem("reader") == null) this.reader = 1;
      else this.reader = Number(window.localStorage.getItem("reader"));
    },

    initUtter() {
      this.audio = new Audio();
      if (this.hasSpeechSynthesis) {
        this.utterThis = new SpeechSynthesisUtterance();
      }
    },

    firstClick() {
      if (
        this.isIphone &&
        !(this.isUtterTransLineFirstly == false && this.isSystemTTS == "No")
      ) {
        if (this.isSystemTTS == "Yes") this.utterTransLine();
        if (this.isSystemTTS == "No" && this.audio) this.audio.muted = true;
        this.currentMedia.play();
        this.currentMedia.currentTime =
          this.srtSubtitles[this.sentenceIndex - 1].startTime;
        this.currentMedia.muted = true;
        setTimeout(() => {
          this.currentMedia.muted = false;
          if (this.audio) this.audio.muted = false;
          this.cleanUp2();
          this.cleanUp1();

          if (
            this.firstMount &&
            (this.currentMedia.currentTime <
              this.srtSubtitles[this.sentenceIndex - 1].startTime - 0.2 ||
              this.currentMedia.currentTime >
                this.srtSubtitles[this.sentenceIndex - 1].startTime + 0.2)
          ) {
            window.sessionStorage.setItem("isBrowserHiJack", true);
            location.reload();
          }
        }, 1);
      }
      this.isFirstClick = false;
    },

    singleModePlay() {
      this.cleanUp1();
      if (!this.isReadyToPlay) return;

      if (this.replayFromStart) {
        if (
          this.isUtterTransLine &&
          this.isUtterTransLineFirstly &&
          this.canUtter
        ) {
          this.currentMedia.currentTime =
            this.srtSubtitles[this.sentenceIndex - 1].startTime;
          this.utterTransLine();
        } else this.loopPlay();
      } else {
        if (
          (this.isUtterTransLine &&
            this.isUtterTransLineFirstly &&
            this.canUtter &&
            !this.playInProcess) ||
          this.utterInProcess
        ) {
          this.currentMedia.currentTime =
            this.srtSubtitles[this.sentenceIndex - 1].startTime;
          this.utterTransLine();
        } else this.loopPlay();
      }
    },

    chooseSentence(index, indexWordList) {
      this.sentenceIndex = index;
      this.sentenceIndex = index + 1;
      if (this.showNewWordList && !this.withTrans) {
        if (this.newWordList.length > 0)
          this.newWordList[indexWordList].showTrans = true;
        this.indexOfNewWordList = indexWordList;
      }
      this.cleanUp1();
      this.cleanUp2();
      if (this.isFavOnPlay && this.showNewWordList) return;
      this.click();
    },

    switchShowList() {
      if (!this.showSubtitleList && !this.showNewWordList && !this.withTrans) {
        this.showSubtitleList = true;
      } else if (
        this.showSubtitleList &&
        !this.showNewWordList &&
        !this.withTrans &&
        !this.isFavOnPlay
      ) {
        this.showSubtitleList = false;
        this.showNewWordList = true;
      } else if (
        this.showSubtitleList &&
        !this.showNewWordList &&
        !this.withTrans &&
        this.isFavOnPlay
      ) {
        this.showSubtitleList = false;
      } else if (
        !this.showSubtitleList &&
        this.showNewWordList &&
        !this.withTrans
      ) {
        this.withTrans = true;
      } else if (
        !this.showSubtitleList &&
        this.showNewWordList &&
        this.withTrans
      ) {
        if (this.newWordList.length > 0)
          this.newWordList[this.indexOfNewWordList].showTrans = false;
        this.showNewWordList = false;
        this.withTrans = false;
      }
      if (this.showSubtitleList) {
        setTimeout(() => {
          document
            .getElementById(this.sentenceIndex)
            .scrollIntoView({ block: "center", behavior: "smooth" });
        }, 100);
      }
    },

    testTTSurl() {
      let transLineContent =
        this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[
          this.lineNumOfTrans - 1
        ];
      let text =
        transLineContent !== undefined && transLineContent !== " "
          ? transLineContent
          : "no content";
      let ttsFullUrl = this.TTSurl + text;
      fetch(ttsFullUrl)
        .then(() => {
          this.audio.src = ttsFullUrl;
          this.audio.play();
          this.audio.addEventListener("ended", this.endTestUtter, false);
        })
        .catch((error) => console.error("Error Uttering Trans Line:", error));
    },
    testTTSVoice() {
      if (this.isUtterTransLine && this.isSystemTTS == "Yes") {
        this.cleanUp1();
        this.cleanUp2();
        let transLineContent =
          this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[
            this.lineNumOfTrans - 1
          ];
        this.utterThis.text =
          transLineContent !== undefined &&
          transLineContent !== " " &&
          transLineContent !== ""
            ? transLineContent
            : "no content";
        if (this.langInTransLine == "") {
          this.langInTransLine = navigator.language || navigator.userLanguage;
        }
        this.utterThis.lang = this.langInTransLine;
        this.utterThis.rate = this.speedOfUtter;
        let voices = window.speechSynthesis.getVoices();
        let formattedLang =
          this.langInTransLine.substring(0, 3) +
          this.langInTransLine.substring(3).toUpperCase();
        this.utterThis.voice = voices.filter(function (voice) {
          return voice.lang.includes(formattedLang);
        })[this.reader - 1];
        window.speechSynthesis.speak(this.utterThis);
        this.utterThis.onend = () => {
          this.cleanUp2();
          this.cleanUp1();
        };
      }
    },
    endTestUtter() {
      this.audio.removeEventListener("ended", this.endTestUtter, false);
      this.cleanUp2();
      this.cleanUp1();
    },
    utterTransLine() {
      this.utterInProcess = true;
      if (this.isUtterTransLine && this.isSystemTTS == "Yes") {
        let transLineContent =
          this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[
            this.lineNumOfTrans - 1
          ];
        this.utterThis.text =
          transLineContent !== undefined &&
          transLineContent !== " " &&
          transLineContent !== ""
            ? transLineContent
            : "no content";
        if (this.langInTransLine == "") {
          this.langInTransLine = navigator.language || navigator.userLanguage;
        }
        this.utterThis.lang = this.langInTransLine;
        this.utterThis.rate = this.speedOfUtter;
        let voices = window.speechSynthesis.getVoices();
        let formattedLang =
          this.langInTransLine.substring(0, 3) +
          this.langInTransLine.substring(3).toUpperCase();
        this.utterThis.voice = voices.filter(function (voice) {
          return voice.lang.includes(formattedLang);
        })[this.reader - 1];
        window.speechSynthesis.speak(this.utterThis);
        this.utterThis.onend = () => {
          this.endUtter();
        };
      } else if (this.isUtterTransLine && this.isSystemTTS == "No") {
        let transLineContent =
          this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[
            this.lineNumOfTrans - 1
          ];
        let text =
          transLineContent !== undefined &&
          transLineContent !== " " &&
          transLineContent !== ""
            ? transLineContent
            : "no content";
        let ttsFullUrl = this.TTSurl + text;
        fetch(ttsFullUrl)
          .then(() => {
            this.audio.src = ttsFullUrl;
            this.audio.play();
            this.audio.addEventListener("ended", this.endUtter, false);
          })
          .catch((error) => console.error("Error Uttering Trans Line:", error));
      }
    },

    endUtter() {
      this.audio.removeEventListener("ended", this.endUtter, false);
      if (
        this.isEditSubandNotes &&
        this.isUtterTransLine &&
        !this.isUtterTransLineFirstly
      ) {
        this.cleanUp1();
        this.cleanUp2();
        return;
      }
      if (
        this.isUtterTransLine &&
        this.isUtterTransLineFirstly &&
        this.isPauseAfterFirstDone
      ) {
        this.pauseAfterFirstDone = true;
        this.utterInProcess = false;
        return;
      } else if (
        (!this.autoPlayNext || this.showNewWordList) &&
        this.isUtterTransLine &&
        !this.isUtterTransLineFirstly
      ) {
        this.utterInProcess = false;
        this.cleanUp1();
        return;
      } else {
        this.timeOutId = setTimeout(() => {
          this.utterInProcess = false;
          if (
            this.isUtterTransLine &&
            !this.isUtterTransLineFirstly &&
            this.sentenceIndex < this.srtSubtitles.length
          ) {
            if (
              this.autoPlayNext &&
              !this.isEditSubandNotes &&
              !this.showNewWordList
            ) {
              this.sentenceIndex = this.sentenceIndex + 1;
              if (
                !this.autoPlay ||
                (this.isFavOnPlay && this.isPlayFullFavList)
              ) {
                this.cleanUp1();
                return;
              }
              this.singleModePlay();
            } else {
              this.cleanUp1();
              this.cleanUp2();
              return;
            }
          } else if (
            this.isUtterTransLine &&
            !this.isUtterTransLineFirstly &&
            this.sentenceIndex == this.srtSubtitles.length
          ) {
            this.cleanUp1();
            return;
          } else {
            this.loopPlay();
          }
        }, this.pauseTimeTransLine * 1000);
      }
    },

    switchSubtitle() {
      this.ShowSwitchSubtitle = true;
      if (this.timeOutId1) clearTimeout(this.timeOutId1);
      this.subtitleLang = this.subtitleLang + 1;
      if (this.subtitleLang == 9) this.subtitleLang = 1;
      this.switchSubtitleMini();
      this.timeOutId1 = setTimeout(() => {
        this.ShowSwitchSubtitle = false;
      }, 3000);
    },
    switchSubtitleMini() {
      if (this.subtitleLang == 1) {
        this.isShowLine1 = true;
        this.isShowLine2 = true;
        this.isShowLine3 = false;
      } else if (this.subtitleLang == 2) {
        this.isShowLine1 = true;
        this.isShowLine2 = true;
        this.isShowLine3 = true;
      } else if (this.subtitleLang == 3) {
        this.isShowLine1 = false;
        this.isShowLine2 = false;
        this.isShowLine3 = true;
      } else if (this.subtitleLang == 4) {
        this.isShowLine1 = true;
        this.isShowLine2 = false;
        this.isShowLine3 = false;
      } else if (this.subtitleLang == 5) {
        this.isShowLine1 = false;
        this.isShowLine2 = true;
        this.isShowLine3 = false;
      } else if (this.subtitleLang == 6) {
        this.isShowLine1 = true;
        this.isShowLine2 = false;
        this.isShowLine3 = true;
      } else if (this.subtitleLang == 7) {
        this.isShowLine1 = false;
        this.isShowLine2 = true;
        this.isShowLine3 = true;
      } else if (this.subtitleLang == 8) {
        this.isShowLine1 = false;
        this.isShowLine2 = false;
        this.isShowLine3 = false;
      }
    },

    resetTTSurl() {
      this.TTSurl =
        "https://dds.dui.ai/runtime/v1/synthesize?voiceId=xijunm&speed=1.1&volume=100&text=";
    },
    cleanUp1() {
      if (window.speechSynthesis) window.speechSynthesis.cancel();
      if (this.currentMedia) this.currentMedia.pause();
      if (this.audio) this.audio.pause();
      if (this.intervalId) clearInterval(this.intervalId);
      if (this.timeOutId) clearTimeout(this.timeOutId);
      if (this.currentMedia && this.currentMedia.removeEventListener) {
        this.currentMedia.removeEventListener("timeupdate", this.syncSub);
      }
      this.showAddNew = false;
      this.showEditNew = false;
      this.newTranslation = "";
    },
    cleanUp2() {
      if (this.pauseAfterFirstDone) this.pauseAfterFirstDone = false;
      if (this.playInProcess) this.playInProcess = false;
      if (this.utterInProcess) this.utterInProcess = false;
      this.playCount = 0;
    },

    highLightItem(event) {
      if (event.target && event.target.nodeName == "P") {
        event.target.style.color = "blue";
      }
    },

    playFavList() {
      this.cleanUp2();
      this.cleanUp1();
      this.showSubtitleList = false;
      if (this.showNewWordList) {
        if (this.newWordList.length > 0)
          this.newWordList[this.indexOfNewWordList].showTrans = false;
        this.showNewWordList = false;
      }
      this.withTrans = false;
      if (!this.isFavOnPlay) {
        window.sessionStorage.setItem("lastSentenceIndex", this.sentenceIndex);
      }
      this.isFavOnPlay = !this.isFavOnPlay;
      if (this.isFavOnPlay) {
        if (this.isEditSubandNotes) this.switchEditSubandNote();
        this.isFav = true;
        this.sentenceIndex = 1;
        if (!this.isPlayFullFavList) {
          this.singleModePlay();
        }
      } else {
        this.calcFav();
        if (this.isPlayFullFavList) {
          this.sentenceIndex = Number(
            window.sessionStorage.getItem("lastSentenceIndex")
          );
        } else {
          if (this.isFirstClick) this.firstClick();
          this.singleModePlay();
        }
      }
    },

    formatAll(x) {
      x = x.replace(/\n\n$/, "");
      if (x.includes("\r\n")) x = x.replaceAll("\r\n", "\n");
      if (x.includes("\n\n\n\n")) x = x.replaceAll("\n\n\n\n", "\n\n");
      if (x.includes("\n\n\n")) x = x.replaceAll("\n\n\n", "\n\n");
      if (x.includes("\t\t")) x = x.replaceAll("\t\t", "\n");
      x = x.replaceAll(/^\s*\r?\n|\r?\n\s*$/g, "");
      x = x.replace(/^\n+|\n+$/g, "");

      for (var i = 0; i < x.split("\n\n").length; ++i) {
        var ni = i + 1;
        let nCont = x
          .split("\n\n")
          [i].replace(x.split("\n\n")[i].split("\n")[0] + "\n", ni + "\n");
        x = x.replace(x.split("\n\n")[i], nCont);
      }

      return x;
    },

    switchIsFav() {
      let srtUrl = api.getDownloadURL(this.reqF, true);
      let originRaw = "";
      if (srtUrl && this.isMediaType == 1) {
        originRaw = srtUrl.replace(".srt", ".mp3");
      } else if (srtUrl && this.isMediaType == 2) {
        originRaw = srtUrl.replace(".srt", ".mp4");
      } else originRaw = "";
      if (this.isReadyToPlay || (this.isFavOnPlay && this.isPlayFullFavList)) {
        this.isFav = !this.isFav;
        if (this.isFav) {
          //add a fav
          var fav = {
            rawPath: this.reqF.name,
            mediaName: this.mediaName,
            originalRawPath: originRaw.split("?")[0].split("/raw/")[1],
            startTime: this.srtSubtitles[this.sentenceIndex - 1].startTime,
            endTime: this.srtSubtitles[this.sentenceIndex - 1].endTime,
            content: this.srtSubtitles[this.sentenceIndex - 1].content,
          };
          this.favList.push(fav);
          this.save();
        } else {
          //remove a fav
          if (this.isFavOnPlay) {
            this.cleanUp2();
            this.cleanUp1();
          }

          if (this.fromMerge) {
            this.fromMerge = false;
            var nowStartTime1 = this.srtSubtitles[this.sentenceIndex].startTime;
            this.favList = this.favList.filter(function (item) {
              return item.startTime !== nowStartTime1;
            });
          }

          if (this.notFromStarttimeTempChg) {
            var nowStartTime =
              this.srtSubtitles[this.sentenceIndex - 1].startTime;
            this.favList = this.favList.filter(function (item) {
              return item.startTime !== nowStartTime;
            });
          } else {
            this.notFromStarttimeTempChg = true;
            var nowEndTime = this.srtSubtitles[this.sentenceIndex - 1].endTime;
            this.favList = this.favList.filter(function (item) {
              return item.endTime !== nowEndTime;
            });
          }

          this.save();
          if (this.isFavOnPlay) {
            if (this.srtSubtitles.length < 1) {
              this.isFavOnPlay = false;
              return;
            }
            if (this.sentenceIndex > this.srtSubtitles.length) {
              this.sentenceIndex = this.sentenceIndex - 1;
            }
            this.isFav = true;
          }
        }
      }
    },

    switchEditSubandNote() {
      this.isEditSubandNotes = !this.isEditSubandNotes;
      if (this.isEditSubandNotes) {
        this.onRUdo = true;
        setTimeout(() => {
          this.onRUdo = false;
        }, 1000);
        this.cleanUp1();
        this.cleanUp2();
        this.isShowLine1 = true;
        this.isShowLine2 = true;
        this.isShowLine3 = true;
        this.startTimeTemp =
          this.srtSubtitles[this.sentenceIndex - 1].startTime;
        this.endTimeTemp = this.srtSubtitles[this.sentenceIndex - 1].endTime;
        this.subFirstLine =
          this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[0];
        this.subSecLine =
          this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[1];
        this.note =
          this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[2];
      } else this.switchSubtitleMini();
    },

    startTimeAdd() {
      let temp = (this.startTimeTemp + 0.05).toFixed(3);
      this.startTimeTemp = parseFloat(temp);
    },

    startTimeMinus() {
      let temp = (this.startTimeTemp - 0.05).toFixed(3);
      this.startTimeTemp = parseFloat(temp);
    },

    endTimeAdd() {
      let temp = (this.endTimeTemp + 0.05).toFixed(3);
      this.endTimeTemp = parseFloat(temp);
    },
    endTimeMinus() {
      let temp = (this.endTimeTemp - 0.05).toFixed(3);
      this.endTimeTemp = parseFloat(temp);
    },

    showConfirm(index) {
      if (this.confirmType == "fetch") {
        var userConfirmation = window.confirm(
          this.$t("repeater.noFavoriteFile", {
            favFileName: this.favFileName,
          })
        );
        if (userConfirmation) {
          this.favList = [];
          if (this.isAutoDetectLang) this.autoDetectLangInTrans();
          if (!this.hasSpeechSynthesis) {
            this.isSystemTTS = "No";
          }
          this.save();
          setTimeout(() => {
            this.currentMedia.pause();
          }, 1);
        } else {
          this.cleanUp1();
          this.close();
        }
      }

      if (this.confirmType == "fetchRevision") {
        var userConfirmationRevision = window.confirm(
          this.$t("repeater.removeRevisionConfirm")
        );
        if (userConfirmationRevision) {
          this.reviseData.splice(index, 1);
        } else {
          return;
        }
      }

      if (this.confirmType == "wrongSrc") {
        window.confirm(this.$t("repeater.wrongSrc"));
        this.cacheCleanUp();
        setTimeout(() => {
          if (this.isFavOnPlay && this.isPlayFullFavList) {
            this.switchIsFav();
          }
          return;
        }, 500);
      }

      if (this.confirmType == "delete") {
        var userConfirmationDelete = window.confirm(
          this.$t("repeater.confirmDelete")
        );
        if (userConfirmationDelete) {
          this.deleteSentence();
        } else {
          return;
        }
      }
      if (this.confirmType == "merge") {
        var userConfirmationMerge = window.confirm(
          this.$t("repeater.confirmMerge")
        );
        if (userConfirmationMerge) {
          this.mergeSentence();
        } else {
          return;
        }
      }
      if (this.confirmType == "add") {
        var userConfirmationAdd = window.confirm(
          this.$t("repeater.confirmAdd")
        );
        if (userConfirmationAdd) {
          this.addSentence();
        } else {
          return;
        }
      }
    },

    onSingle() {
      this.isSingle = !this.isSingle;
      if (!this.isSingle) {
        this.isEditSubandNotes = false;
        this.cleanUp2();
        this.cleanUp1();
        this.regularPlay();
        this.currentMedia.currentTime = 0;
        this.currentMedia.addEventListener("focus", this.removeFocus);
      }
      if (this.isSingle) {
        this.cleanUp2();
        this.isEmpty = false;
        if (this.isFirstClick) this.firstClick();
        this.singleModePlay();
        this.currentMedia.currentTime =
          this.srtSubtitles[this.sentenceIndex - 1].startTime;
        this.currentMedia.removeEventListener("focus", this.removeFocus);
      }
    },

    removeFocus() {
      this.currentMedia.blur();
    },

    cacheCleanUp() {
      let vmm = this;
      localforage
        .clear()
        .then(function () {
          window.localStorage.removeItem("cKeys");
          console.log("Database is now empty.");
          vmm.numOfKeys = 0;
          vmm.playFromCache = false;
          vmm.cachedKeys = "";
        })
        .catch(function (err) {
          console.log(err);
        });
    },

    onSetting() {
      this.cachedNumber();
      this.getReader();
      this.isSetting = !this.isSetting;
      if (this.isSetting) {
        this.cleanUp2();
        this.cleanUp1();
      } else {
        setTimeout(() => {
          this.cleanUp2();
          if (this.isFirstClick) this.firstClick();
          this.singleModePlay();
        }, 1);
      }
      return;
    },
    click: function () {
      if (this.isFirstClick) this.firstClick();
      this.touches++;
      this.cleanUp1();
      if (this.isEditSubandNotes) {
        if (
          document.getElementById("editArea0") &&
          document.getElementById("editArea0").contains(document.activeElement)
        ) {
          document.getElementById("editArea0").blur();
        } else if (
          document.getElementById("editArea00") &&
          document.getElementById("editArea00").contains(document.activeElement)
        ) {
          document.getElementById("editArea00").blur();
        } else if (
          document.getElementById("editArea1") &&
          document.getElementById("editArea1").contains(document.activeElement)
        ) {
          document.getElementById("editArea1").blur();
        } else if (
          document.getElementById("editArea2") &&
          document.getElementById("editArea2").contains(document.activeElement)
        ) {
          document.getElementById("editArea2").blur();
        } else if (
          document.getElementById("editArea3") &&
          document.getElementById("editArea3").contains(document.activeElement)
        ) {
          document.getElementById("editArea3").blur();
        }
      }
      if (this.touches == 1) {
        if (this.isSingle) {
          setTimeout(() => {
            if (this.pauseAfterFirstDone) {
              this.pauseAfterFirstDone = false;
              if (this.isUtterTransLineFirstly) this.loopPlay();
              else this.utterTransLine();
            } else {
              this.singleModePlay();
            }
          }, 10);
        } else {
          setTimeout(() => {
            this.regularPlay();
            this.currentMedia.currentTime =
              this.srtSubtitles[this.sentenceIndex - 1].startTime;
          }, 1);
        }
      }
      setTimeout(() => {
        if (this.touches == 2) {
          //double click
          this.cleanUp1();
          this.touches = 0;
          return;
        }
        this.touches = 0;
      }, 300);
    },

    dblClick(event) {
      event.preventDefault();
      this.cleanUp1();
      this.touches = 0;
      return;
    },

    startDrag(event) {
      event.preventDefault();
      this.startDragS(event);
    },
    startDragS(event) {
      if (!this.isReadyToPlay || this.isTouchDevice) return;
      this.isSetting = false;
      this.showRevision = false;
      this.showSubtitleList = false;
      if (this.showNewWordList) {
        if (this.newWordList.length > 0)
          this.newWordList[this.indexOfNewWordList].showTrans = false;
        this.showNewWordList = false;
      }
      this.withTrans = false;
      this.startTime = new Date().getTime();
      this.startX = event.clientX;
      this.startY = event.clientY;
    },
    endDrag(event) {
      event.preventDefault();
      this.endDragS(event);
    },
    endDragS(event) {
      if (!this.isReadyToPlay || this.isTouchDevice) return;
      this.timeDiff = new Date().getTime() - this.startTime;
      this.distanceX = event.clientX - this.startX;
      this.distanceY = event.clientY - this.startY;
      if (
        this.isReadyToPlay &&
        this.timeDiff < 300 &&
        Math.abs(this.distanceX) > Math.abs(this.distanceY) &&
        Math.abs(this.distanceX) > 60
      ) {
        this.checkNav(this.distanceX, "SWITCHIMG");
        return;
      }
      if (
        this.timeDiff < 300 &&
        Math.abs(this.distanceX) < Math.abs(this.distanceY) &&
        Math.abs(this.distanceY) > 100
      ) {
        this.checkNav(this.distanceY, "VERTICAL");
        return;
      }
      if (
        window.getSelection().toString() &&
        window.getSelection().toString() !== "" &&
        window.getSelection().toString() !== " " &&
        document.getElementById("subArea") &&
        document.getElementById("subArea").contains(event.target) &&
        !this.isFavOnPlay
      ) {
        this.cleanUp1();
        this.cleanUp2();
        this.newWord = window.getSelection().toString();
        this.showAddNew = true;
      } else {
        window.getSelection().removeAllRanges();
        this.showAddNew = false;
        this.showEditNew = false;
        this.click();
      }
    },

    startTouch(event) {
      event.preventDefault();
      this.startTouchS(event);
    },
    startTouchS(event) {
      if (!this.isReadyToPlay) return;
      this.isSetting = false;
      this.showRevision = false;
      this.showSubtitleList = false;
      if (this.showNewWordList) {
        if (this.newWordList.length > 0)
          this.newWordList[this.indexOfNewWordList].showTrans = false;
        this.showNewWordList = false;
      }
      this.withTrans = false;
      this.startTime = new Date().getTime();
      this.startX = event.touches[0].clientX;
      this.startY = event.touches[0].clientY;
    },
    endTouch(event) {
      event.preventDefault();
      this.endTouchS(event);
    },
    endTouchS(event) {
      if (!this.isReadyToPlay) return;
      this.timeDiff = new Date().getTime() - this.startTime;
      this.distanceX = event.changedTouches[0].clientX - this.startX;
      this.distanceY = event.changedTouches[0].clientY - this.startY;
      if (
        this.isReadyToPlay &&
        this.timeDiff < 300 &&
        Math.abs(this.distanceX) > Math.abs(this.distanceY) &&
        Math.abs(this.distanceX) > 30
      ) {
        this.checkNav(this.distanceX, "SWITCHIMG");
        return;
      }
      if (
        this.timeDiff < 300 &&
        Math.abs(this.distanceX) < Math.abs(this.distanceY) &&
        Math.abs(this.distanceY) > 70
      ) {
        this.checkNav(this.distanceY, "VERTICAL");
        return;
      }
      if (
        window.getSelection().toString() &&
        window.getSelection().toString() !== "" &&
        window.getSelection().toString() !== " " &&
        document.getElementById("subArea") &&
        document.getElementById("subArea").contains(event.target) &&
        !this.isFavOnPlay
      ) {
        this.cleanUp1();
        this.cleanUp2();
        this.newWord = window.getSelection().toString();
        this.showAddNew = true;
      } else {
        window.getSelection().removeAllRanges();
        this.showAddNew = false;
        this.showEditNew = false;
        this.click();
      }
    },

    checkNav(x, mode) {
      if (x > 0 && mode == "SWITCHIMG" && this.sentenceIndex >= 1) {
        this.cleanUp2();
        this.cleanUp1();
        if (this.sentenceIndex == 1) return;
        this.sentenceIndex = this.sentenceIndex - 1;
        if (
          (this.isSingle && !this.autoPlay) ||
          (this.isFavOnPlay && this.isPlayFullFavList)
        )
          return;
        if (this.isSingle) {
          if (this.isFirstClick) return;
          this.click();
        } else {
          setTimeout(() => {
            this.regularPlay();
            this.currentMedia.currentTime =
              this.srtSubtitles[this.sentenceIndex - 1].startTime;
          }, 1);
        }
        return;
      } else if (
        x < 0 &&
        mode == "SWITCHIMG" &&
        this.sentenceIndex <= this.srtSubtitles.length
      ) {
        this.cleanUp2();
        this.cleanUp1();
        if (this.sentenceIndex == this.srtSubtitles.length) return;
        this.sentenceIndex = this.sentenceIndex + 1;
        if (
          (this.isSingle && !this.autoPlay) ||
          (this.isFavOnPlay && this.isPlayFullFavList)
        )
          return;
        if (this.isSingle) {
          if (this.isFirstClick) return;
          this.click();
        } else {
          setTimeout(() => {
            this.regularPlay();
            this.currentMedia.currentTime =
              this.srtSubtitles[this.sentenceIndex - 1].startTime;
          }, 1);
        }
        return;
      } else if (
        this.isReadyToPlay &&
        x < 0 &&
        mode == "VERTICAL" &&
        ((!this.isFavOnPlay && !this.isFav) || (this.isFavOnPlay && this.isFav))
      ) {
        this.switchIsFav();
        return;
      } else if (x > 0 && mode == "VERTICAL") {
        this.close();
        return;
      }
    },

    regularPlay() {
      this.isSingle = false;
      if (!this.isReadyToPlay) return;
      const media = this.currentMedia;
      if (media) {
        media.playbackRate = 1.0;
        media
          .play()
          .then(() => {
            media.addEventListener("timeupdate", this.syncSub);
          })
          .catch((error) => {
            console.error(error);
          });
      }
    },
    syncSub() {
      const media = this.currentMedia;
      for (var i = 0; i < this.srtSubtitles.length; ++i) {
        if (
          media.currentTime >= this.srtSubtitles[i].startTime &&
          media.currentTime <= this.srtSubtitles[i].endTime
        ) {
          this.sentenceIndex = i + 1;
          this.isEmpty = false;
          return;
        } else if (
          i > 0 &&
          i < this.srtSubtitles.length - 1 &&
          media.currentTime < this.srtSubtitles[i + 1].startTime &&
          media.currentTime > this.srtSubtitles[i].endTime
        ) {
          this.sentenceIndex = i + 1;
        } else if (media.currentTime < this.srtSubtitles[0].startTime) {
          this.sentenceIndex = 1;
        } else if (
          i == this.srtSubtitles.length - 1 &&
          media.currentTime > this.srtSubtitles[i].endTime
        ) {
          this.sentenceIndex = i + 1;
        }

        if (i == this.srtSubtitles.length - 1) {
          this.isEmpty = true;
        }
      }
    },

    playSection() {
      if (this.currentMedia && this.currentMedia.removeEventListener) {
        this.currentMedia.removeEventListener("timeupdate", this.syncSub);
      }
      if (!this.isReadyToPlay) return;
      if (this.currentMedia) {
        if (this.isIphone) {
          setTimeout(() => {
            this.currentMedia.play();
          }, 20);
        } else this.currentMedia.play();
        if (this.currentSpeed.split(",")[this.playCount]) {
          this.currentMedia.playbackRate = Number(
            this.currentSpeed.split(",")[this.playCount]
          );
        } else {
          this.currentMedia.playbackRate = 1;
        }
        this.sessionLength =
          (this.srtSubtitles[this.sentenceIndex - 1].endTime -
            this.srtSubtitles[this.sentenceIndex - 1].startTime +
            2) /
          this.currentMedia.playbackRate;
        let playLength =
          (this.srtSubtitles[this.sentenceIndex - 1].endTime -
            this.srtSubtitles[this.sentenceIndex - 1].startTime) /
            this.currentMedia.playbackRate -
          0.2;
        this.currentMedia.currentTime =
          this.srtSubtitles[this.sentenceIndex - 1].startTime;
        this.startTime = new Date().getTime();
        this.timeOutId = setTimeout(() => {
          this.isEnd();
        }, playLength * 1000);
      }
    },
    isEnd() {
      this.intervalId = setInterval(() => {
        this.sessionEnd();
      }, 10);
    },

    sessionEnd() {
      this.timeDiff = (new Date().getTime() - this.startTime) / 1000;
      if (this.timeDiff > this.sessionLength) {
        if (this.intervalId) {
          clearInterval(this.intervalId);
        }
        this.isSlowInternet = true;
        setTimeout(() => {
          this.isSlowInternet = false;
        }, 2000);
        this.currentMedia.pause();
        this.playSection();
        return;
      }
      if (
        this.currentMedia.currentTime >=
        this.srtSubtitles[this.sentenceIndex - 1].endTime
      ) {
        if (this.intervalId) {
          clearInterval(this.intervalId);
        }
        this.currentMedia.pause();
        if (this.playCount >= this.repeatTimes - 1) {
          if (this.replayFromStart) this.playCount = 0;
          this.cleanUp1();
          if (
            this.isUtterTransLine &&
            !this.isUtterTransLineFirstly &&
            this.isPauseAfterFirstDone
          ) {
            this.playCount = 0;
            this.playInProcess = false;
            this.pauseAfterFirstDone = true;
            return;
          }
          this.timeOutId = setTimeout(() => {
            this.playCount = 0;
            this.playInProcess = false;
            if (
              this.isUtterTransLine &&
              !this.isUtterTransLineFirstly &&
              this.canUtter
            ) {
              this.utterTransLine();
            } else {
              if (
                this.autoPlayNext &&
                !this.isEditSubandNotes &&
                !this.showNewWordList &&
                this.sentenceIndex < this.srtSubtitles.length
              ) {
                this.sentenceIndex = this.sentenceIndex + 1;
                if (
                  !this.autoPlay ||
                  (this.isFavOnPlay && this.isPlayFullFavList)
                ) {
                  this.cleanUp1();
                  return;
                }
                this.singleModePlay();
              } else {
                this.cleanUp1();
                this.cleanUp2();
              }
            }
          }, this.interval * 1000);
        } else {
          if (window.speechSynthesis) window.speechSynthesis.cancel();
          if (this.currentMedia) this.currentMedia.pause();
          if (this.timeOutId) clearTimeout(this.timeOutId);
          if (this.currentMedia && this.currentMedia.removeEventListener) {
            this.currentMedia.removeEventListener("timeupdate", this.syncSub);
          }
          this.timeOutId = setTimeout(() => {
            this.playCount++;
            this.loopPlay();
          }, this.interval * 1000);
        }
      }
    },
    loopPlay() {
      this.isSingle = true;
      this.playInProcess = true;
      if (this.timeOutId) clearTimeout(this.timeOutId);
      if (this.currentMedia) this.currentMedia.pause();
      if (this.repeatTimes < 1) {
        if (this.intervalId) {
          clearInterval(this.intervalId);
        }
        if (this.replayFromStart) this.playCount = 0;
        this.cleanUp1();
        if (
          this.isUtterTransLine &&
          !this.isUtterTransLineFirstly &&
          this.isPauseAfterFirstDone
        ) {
          this.playCount = 0;
          this.playInProcess = false;
          this.pauseAfterFirstDone = true;
          return;
        }
        this.playCount = 0;
        this.playInProcess = false;
        if (
          this.isUtterTransLine &&
          !this.isUtterTransLineFirstly &&
          this.canUtter
        ) {
          this.utterTransLine();
        } else {
          if (
            this.autoPlayNext &&
            !this.isEditSubandNotes &&
            !this.showNewWordList &&
            this.sentenceIndex < this.srtSubtitles.length
          ) {
            this.sentenceIndex = this.sentenceIndex + 1;
            if (
              !this.autoPlay ||
              (this.isFavOnPlay && this.isPlayFullFavList)
            ) {
              this.cleanUp1();
              return;
            }
            this.singleModePlay();
          } else {
            this.cleanUp1();
            this.cleanUp2();
          }
        }
        return;
      }
      this.playSection();
    },
    autoDetectLangInTrans() {
      if (
        !this.isEnglishLine1 &&
        !(
          !this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[0] ||
          this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[0] ==
            " "
        )
      ) {
        this.isUtterTransLine = true;
        this.lineNumOfTrans = 1;
      } else if (
        !this.isEnglishLine2 &&
        !(
          !this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[1] ||
          this.srtSubtitles[this.sentenceIndex - 1].content.split("\r\n")[1] ==
            " "
        )
      ) {
        this.isUtterTransLine = true;
        this.lineNumOfTrans = 2;
      } else this.isUtterTransLine = false;
    },

    save() {
      if (
        (!this.isReadyToPlay &&
          !(this.isFavOnPlay && this.isPlayFullFavList)) ||
        this.notSaveFav
      )
        return;
      let customConfig =
        "customConfig" +
        "::" +
        JSON.stringify(this.repeatTimes) +
        "::" +
        JSON.stringify(this.interval) +
        "::" +
        JSON.stringify(this.autoPlayNext) +
        "::" +
        JSON.stringify(this.timeStampChangeStart) +
        "::" +
        JSON.stringify(this.currentSpeed) +
        "::" +
        JSON.stringify(this.subtitleLang) +
        "::" +
        JSON.stringify(this.isUtterTransLine) +
        "::" +
        JSON.stringify(this.pauseTimeTransLine) +
        "::" +
        JSON.stringify(this.speedOfUtter) +
        "::" +
        JSON.stringify(this.isUtterTransLineFirstly) +
        "::" +
        JSON.stringify(this.langInTransLine) +
        "::" +
        JSON.stringify(this.lineNumOfTrans) +
        "::" +
        JSON.stringify(this.isAutoDetectLang) +
        "::" +
        JSON.stringify(this.isPauseAfterFirstDone) +
        "::" +
        JSON.stringify(this.autoPlay) +
        "::" +
        JSON.stringify(this.isSystemTTS) +
        "::" +
        JSON.stringify(this.TTSurl) +
        "::" +
        JSON.stringify(this.replayFromStart) +
        "::" +
        JSON.stringify(this.isPlayFullFavList) +
        "::" +
        JSON.stringify(this.timeStampChangeEnd) +
        "::" +
        JSON.stringify(this.reviseData) +
        "::" +
        JSON.stringify(this.revisePlan) +
        "::";

      let favContent =
        customConfig + "Subtitle:" + JSON.stringify(this.favList);
      if (this.serverFav == favContent) return;
      this.tempFavContent = favContent;
      this.saveNow(this.tempFavContent);
    },

    async saveNow(favContent) {
      window.localStorage.setItem(this.favFileName, favContent);
      let vm = this;
      try {
        await api.post("/files/" + this.favFileName, favContent, true);
        vm.serverFav = favContent;
        window.localStorage.removeItem(this.favNotUpload);
      } catch (error) {
        window.localStorage.setItem(this.favNotUpload, "1");
      }
    },

    saveSub() {
      if (this.onRUdo) {
        return;
      }
      if (!this.onEdit) return;
      this.onEdit = false;
      this.tempOldContent = this.reqF.content;
      var tempContent = this.reqF.content;
      var newContent = this.srtSubtitles[this.sentenceIndex - 1].timeStamp;

      if (this.subFirstLine !== undefined) {
        this.subFirstLine = this.subFirstLine.replaceAll("\n", "");
        if (
          this.subFirstLine == "" &&
          ((this.subSecLine !== undefined && this.subSecLine !== "") ||
            (this.note !== undefined && this.note !== ""))
        )
          this.subFirstLine = " ";
        if (this.subFirstLine !== "")
          newContent = newContent + "\n" + this.subFirstLine;
      }
      if (this.subSecLine !== undefined) {
        this.subSecLine = this.subSecLine.replaceAll("\n", "");
        if (
          this.subSecLine == "" &&
          this.note !== undefined &&
          this.note !== ""
        )
          this.subSecLine = " ";
        if (this.subSecLine !== "")
          newContent = newContent + "\n" + this.subSecLine;
      }
      if (
        (this.subSecLine == "" || this.subSecLine == undefined) &&
        this.note !== undefined &&
        this.note !== ""
      )
        newContent = newContent + "\n ";

      if (this.note !== undefined) {
        this.note = this.note.replaceAll("\n", "");
        if (this.note !== "") newContent = newContent + "\n" + this.note;
      }

      var oldContent =
        this.srtSubtitles[this.sentenceIndex - 1].sn +
        "\n" +
        this.srtSubtitles[this.sentenceIndex - 1].timeStamp +
        tempContent
          .split(
            this.srtSubtitles[this.sentenceIndex - 1].sn +
              "\n" +
              this.srtSubtitles[this.sentenceIndex - 1].timeStamp
          )[1]
          .split("\n\n")[0];
      newContent =
        this.srtSubtitles[this.sentenceIndex - 1].sn + "\n" + newContent;
      this.reqF.content = tempContent.replace(oldContent, newContent);
      this.saveSubNow();
    },

    saveSub1() {
      if (this.onRUdo) {
        return;
      }
      if (!this.onEdit) return;
      this.onEdit = false;
      this.tempOldContent = this.reqF.content;
      if (
        this.sentenceIndex > 1 &&
        this.startTimeTemp <= this.srtSubtitles[this.sentenceIndex - 2].endTime
      )
        this.srtSubtitles[this.sentenceIndex - 2].endTime =
          this.startTimeTemp - 0.001;

      if (
        this.sentenceIndex > 1 &&
        this.srtSubtitles[this.sentenceIndex - 2].endTime <=
          this.srtSubtitles[this.sentenceIndex - 2].startTime
      ) {
        this.srtSubtitles[this.sentenceIndex - 2].endTime =
          this.srtSubtitles[this.sentenceIndex - 2].startTime + 0.001;
        this.startTimeTemp =
          this.srtSubtitles[this.sentenceIndex - 2].endTime + 0.001;
      }
      if (this.sentenceIndex == 1 && this.startTimeTemp < 0)
        this.startTimeTemp = 0;
      if (
        this.startTimeTemp >= this.srtSubtitles[this.sentenceIndex - 1].endTime
      )
        this.startTimeTemp =
          this.srtSubtitles[this.sentenceIndex - 1].endTime - 0.001;

      this.startTimeTemp = parseFloat(this.startTimeTemp.toFixed(3));
      var tempContent = this.reqF.content;
      var oldContent =
        this.srtSubtitles[this.sentenceIndex - 1].timeStamp.split(" --> ")[0] +
        " --> ";
      var time = this.convertToHMS(
        this.startTimeTemp * 1000 - this.timeStampChangeStart
      );
      var newContent =
        time.hours +
        ":" +
        time.minutes +
        ":" +
        time.seconds +
        "," +
        time.milliseconds +
        " --> ";

      oldContent =
        this.srtSubtitles[this.sentenceIndex - 1].sn + "\n" + oldContent;
      newContent =
        this.srtSubtitles[this.sentenceIndex - 1].sn + "\n" + newContent;
      tempContent = tempContent.replace(oldContent, newContent);

      if (this.sentenceIndex > 1) {
        this.srtSubtitles[this.sentenceIndex - 2].endTime = parseFloat(
          this.srtSubtitles[this.sentenceIndex - 2].endTime.toFixed(3)
        );
        var oldContent1 =
          " --> " +
          this.srtSubtitles[this.sentenceIndex - 2].timeStamp.split(" --> ")[1];
        var time1 = this.convertToHMS(
          this.srtSubtitles[this.sentenceIndex - 2].endTime * 1000 -
            this.timeStampChangeEnd +
            1
        );
        var newContent1 =
          " --> " +
          time1.hours +
          ":" +
          time1.minutes +
          ":" +
          time1.seconds +
          "," +
          time1.milliseconds;

        oldContent1 =
          this.srtSubtitles[this.sentenceIndex - 2].sn +
          "\n" +
          this.srtSubtitles[this.sentenceIndex - 2].timeStamp.split(
            " --> "
          )[0] +
          oldContent1;
        newContent1 =
          this.srtSubtitles[this.sentenceIndex - 2].sn +
          "\n" +
          this.srtSubtitles[this.sentenceIndex - 2].timeStamp.split(
            " --> "
          )[0] +
          newContent1;
        tempContent = tempContent.replace(oldContent1, newContent1);
      }
      this.reqF.content = tempContent;
      this.saveSubNow();
    },

    saveSub2() {
      if (this.onRUdo) {
        return;
      }
      if (!this.onEdit) return;
      this.onEdit = false;
      this.tempOldContent = this.reqF.content;

      if (
        this.sentenceIndex <= this.srtSubtitles.length - 1 &&
        this.endTimeTemp >= this.srtSubtitles[this.sentenceIndex].startTime
      )
        this.srtSubtitles[this.sentenceIndex].startTime =
          this.endTimeTemp + 0.001;

      if (
        this.sentenceIndex <= this.srtSubtitles.length - 1 &&
        this.srtSubtitles[this.sentenceIndex].startTime >=
          this.srtSubtitles[this.sentenceIndex].endTime
      ) {
        this.srtSubtitles[this.sentenceIndex].startTime =
          this.srtSubtitles[this.sentenceIndex].endTime - 0.001;
        this.endTimeTemp =
          this.srtSubtitles[this.sentenceIndex].startTime - 0.001;
      }

      if (
        this.endTimeTemp <= this.srtSubtitles[this.sentenceIndex - 1].startTime
      )
        this.endTimeTemp =
          this.srtSubtitles[this.sentenceIndex - 1].startTime + 0.001;

      this.endTimeTemp = parseFloat(this.endTimeTemp.toFixed(3));

      var tempContent = this.reqF.content;
      var oldContent =
        " --> " +
        this.srtSubtitles[this.sentenceIndex - 1].timeStamp.split(" --> ")[1];
      var time = this.convertToHMS(
        this.endTimeTemp * 1000 - this.timeStampChangeEnd + 1
      );
      var newContent =
        " --> " +
        time.hours +
        ":" +
        time.minutes +
        ":" +
        time.seconds +
        "," +
        time.milliseconds;

      oldContent =
        this.srtSubtitles[this.sentenceIndex - 1].sn +
        "\n" +
        this.srtSubtitles[this.sentenceIndex - 1].timeStamp.split(" --> ")[0] +
        oldContent;
      newContent =
        this.srtSubtitles[this.sentenceIndex - 1].sn +
        "\n" +
        this.srtSubtitles[this.sentenceIndex - 1].timeStamp.split(" --> ")[0] +
        newContent;
      tempContent = tempContent.replace(oldContent, newContent);

      if (this.sentenceIndex <= this.srtSubtitles.length - 1) {
        this.srtSubtitles[this.sentenceIndex].startTime = parseFloat(
          this.srtSubtitles[this.sentenceIndex].startTime.toFixed(3)
        );
        var oldContent1 =
          this.srtSubtitles[this.sentenceIndex].timeStamp.split(" --> ")[0] +
          " --> ";
        var time1 = this.convertToHMS(
          this.srtSubtitles[this.sentenceIndex].startTime * 1000 -
            this.timeStampChangeStart
        );
        var newContent1 =
          time1.hours +
          ":" +
          time1.minutes +
          ":" +
          time1.seconds +
          "," +
          time1.milliseconds +
          " --> ";
        oldContent1 =
          this.srtSubtitles[this.sentenceIndex].sn + "\n" + oldContent1;
        newContent1 =
          this.srtSubtitles[this.sentenceIndex].sn + "\n" + newContent1;
        tempContent = tempContent.replace(oldContent1, newContent1);
      }
      this.reqF.content = tempContent;
      this.saveSubNow();
    },

    confirmDelete() {
      this.cleanUp1();
      this.cleanUp2();
      this.confirmType = "delete";
      this.showConfirm();
    },

    confirmMerge() {
      this.cleanUp1();
      this.cleanUp2();
      this.confirmType = "merge";
      this.showConfirm();
    },

    confirmAdd() {
      this.cleanUp1();
      this.cleanUp2();
      this.confirmType = "add";
      this.showConfirm();
    },

    async deleteSentence() {
      this.onRUdo = true;
      setTimeout(() => {
        this.onRUdo = false;
      }, 1000);
      if (this.isFav) {
        this.switchIsFav();
      }
      var formatContent = this.reqF.content;
      formatContent = this.formatAll(formatContent);
      this.changeOld[this.historyIndex] = formatContent;

      var textSubtitles = formatContent.split("\n\n");
      formatContent = formatContent.replace(
        textSubtitles[this.sentenceIndex - 1],
        ""
      );
      formatContent = formatContent.replaceAll("\n\n\n\n", "\n\n");
      formatContent = formatContent.replaceAll(/^\s*\r?\n|\r?\n\s*$/g, "");
      const path = url.removeLastDir(this.$route.path);
      this.changeNew[this.historyIndex] = formatContent;

      this.historyIndex = this.historyIndex + 1;
      formatContent = this.formatAll(formatContent);
      this.reqF.content = formatContent;
      window.localStorage.setItem(this.mediaName, formatContent);
      this.cleanUp1();
      this.cleanUp2();
      this.refresh();
      try {
        await api.post(path + "/" + this.reqF.name, formatContent, true);
        window.localStorage.removeItem(this.srtNotUpload);
      } catch (error) {
        window.localStorage.setItem(this.srtNotUpload, "1");
      }
    },

    async mergeSentence() {
      this.onRUdo = true;
      setTimeout(() => {
        this.onRUdo = false;
      }, 1000);
      this.fromMerge = true;
      var hasFav = false;
      if (this.isFav) {
        this.switchIsFav();
        hasFav = true;
      }
      var formatContent = this.reqF.content;
      formatContent = this.formatAll(formatContent);
      this.changeOld[this.historyIndex] = formatContent;
      var textSubtitles = formatContent.split("\n\n");
      var line1 = textSubtitles[this.sentenceIndex].split("\n")[0];
      var line2 =
        textSubtitles[this.sentenceIndex - 1].split("\n")[1].split(" --> ")[0] +
        " --> " +
        textSubtitles[this.sentenceIndex].split("\n")[1].split(" --> ")[1];
      var line3 = " ";
      var line4 = " ";
      var line5 = " ";
      if (
        textSubtitles[this.sentenceIndex - 1].split("\n").length > 2 ||
        textSubtitles[this.sentenceIndex].split("\n").length > 2
      )
        line3 =
          textSubtitles[this.sentenceIndex - 1].split("\n")[2] +
          " " +
          textSubtitles[this.sentenceIndex].split("\n")[2];
      if (
        textSubtitles[this.sentenceIndex - 1].split("\n").length > 3 ||
        textSubtitles[this.sentenceIndex].split("\n").length > 3
      )
        line4 =
          textSubtitles[this.sentenceIndex - 1].split("\n")[3] +
          " " +
          textSubtitles[this.sentenceIndex].split("\n")[3];
      if (
        textSubtitles[this.sentenceIndex - 1].split("\n").length > 4 ||
        textSubtitles[this.sentenceIndex].split("\n").length > 4
      )
        line5 =
          textSubtitles[this.sentenceIndex - 1].split("\n")[4] +
          " " +
          textSubtitles[this.sentenceIndex].split("\n")[4];
      let newOne =
        line1 +
        "\n" +
        line2 +
        "\n" +
        line3.replace("undefined", "") +
        "\n" +
        line4.replace("undefined", "") +
        "\n" +
        line5.replace("undefined", "");

      formatContent = formatContent.replace(
        textSubtitles[this.sentenceIndex - 1],
        ""
      );

      formatContent = formatContent.replace(
        textSubtitles[this.sentenceIndex],
        newOne
      );

      formatContent = formatContent.replaceAll("\n\n\n\n", "\n\n");
      formatContent = formatContent.replaceAll(/^\s*\r?\n|\r?\n\s*$/g, "");
      var srtFullPath = "";
      const path = url.removeLastDir(this.$route.path);
      if (this.onRevision) srtFullPath = this.srtRevisePath;
      else srtFullPath = path + "/" + this.reqF.name;
      this.changeNew[this.historyIndex] = formatContent;

      this.historyIndex = this.historyIndex + 1;
      formatContent = this.formatAll(formatContent);

      this.reqF.content = formatContent;
      this.cleanUp1();
      this.cleanUp2();

      this.sentenceIndex = this.sentenceIndex + 1;
      setTimeout(() => {
        this.sentenceIndex = this.sentenceIndex - 1;
        if (hasFav) this.switchIsFav();
      }, 10);

      window.localStorage.setItem(this.mediaName, formatContent);
      try {
        await api.post(srtFullPath, formatContent, true);
        window.localStorage.removeItem(this.srtNotUpload);
      } catch (error) {
        window.localStorage.setItem(this.srtNotUpload, "1");
      }
    },

    async addSentence() {
      this.onRUdo = true;
      setTimeout(() => {
        this.onRUdo = false;
      }, 1000);
      var formatContent = this.reqF.content;
      formatContent = this.formatAll(formatContent);
      this.changeOld[this.historyIndex] = formatContent;

      var textSubtitles = formatContent.split("\n\n");
      var line1 = "0" + textSubtitles[this.sentenceIndex - 1].split("\n")[0];
      var line2 = " ";
      var tempEndTime = textSubtitles[this.sentenceIndex - 1]
        .split("\n")[1]
        .split(" --> ")[1];
      if (this.sentenceIndex == this.srtSubtitles.length) {
        line2 = tempEndTime + " --> " + tempEndTime.split(",")[0] + "," + "999";
      } else {
        line2 =
          tempEndTime +
          " --> " +
          textSubtitles[this.sentenceIndex].split("\n")[1].split(" --> ")[0];
      }
      var line3 = "First Line";
      var line4 = " ";
      let newLine = line1 + "\n" + line2 + "\n" + line3 + "\n" + line4;
      let newContent = textSubtitles[this.sentenceIndex - 1] + "\n\n" + newLine;
      formatContent = formatContent.replace(
        textSubtitles[this.sentenceIndex - 1],
        newContent
      );

      formatContent = formatContent.replaceAll("\n\n\n\n", "\n\n");
      formatContent = formatContent.replaceAll(/^\s*\r?\n|\r?\n\s*$/g, "");
      var srtFullPath = "";
      const path = url.removeLastDir(this.$route.path);
      if (this.onRevision) srtFullPath = this.srtRevisePath;
      else srtFullPath = path + "/" + this.reqF.name;
      this.changeNew[this.historyIndex] = formatContent;

      this.historyIndex = this.historyIndex + 1;
      formatContent = this.formatAll(formatContent);
      this.reqF.content = formatContent;
      this.cleanUp1();
      this.cleanUp2();
      setTimeout(() => {
        this.sentenceIndex = this.sentenceIndex + 1;
      }, 10);
      window.localStorage.setItem(this.mediaName, formatContent);
      try {
        await api.post(srtFullPath, formatContent, true);
        window.localStorage.removeItem(this.srtNotUpload);
      } catch (error) {
        window.localStorage.setItem(this.srtNotUpload, "1");
      }
    },

    async saveSubNow() {
      if (this.tempOldContent == this.reqF.content) return;
      var srtFullPath = "";
      const path = url.removeLastDir(this.$route.path);
      if (this.onRevision) srtFullPath = this.srtRevisePath;
      else srtFullPath = path + "/" + this.reqF.name;
      if (!this.onRUdo) {
        this.changeOld[this.historyIndex] = this.tempOldContent;
        this.changeNew[this.historyIndex] = this.reqF.content;

        this.historyIndex = this.historyIndex + 1;
      } else this.onRUdo = false;
      for (var i = 0; i < this.reqF.content.split("\n\n").length; ++i) {
        var ni = i + 1;
        let nCont = this.reqF.content
          .split("\n\n")
          [i].replace(
            this.reqF.content.split("\n\n")[i].split("\n")[0] + "\n",
            ni + "\n"
          );
        this.reqF.content = this.reqF.content.replace(
          this.reqF.content.split("\n\n")[i],
          nCont
        );
      }
      if (this.isFav) {
        this.switchIsFav();
        setTimeout(() => {
          this.switchIsFav();
        }, 10);
      }
      window.localStorage.setItem(this.mediaName, this.reqF.content);
      try {
        await api.post(srtFullPath, this.reqF.content, true);
        window.localStorage.removeItem(this.srtNotUpload);
      } catch (error) {
        window.localStorage.setItem(this.srtNotUpload, "1");
      }
    },

    async changeUndo() {
      this.onRUdo = true;
      setTimeout(() => {
        this.onRUdo = false;
      }, 1000);
      var srtFullPath = "";
      const path = url.removeLastDir(this.$route.path);
      if (this.onRevision) srtFullPath = this.srtRevisePath;
      else srtFullPath = path + "/" + this.reqF.name;
      this.historyIndex = this.historyIndex - 1;
      this.reqF.content = this.changeOld[this.historyIndex];
      this.cleanUp1();
      this.cleanUp2();
      if (this.isFav) {
        this.switchIsFav();
        setTimeout(() => {
          this.switchIsFav();
        }, 10);
      }

      this.refresh();
      if (!this.RUdoAlert) {
        this.RUdoAlert = true;
        setTimeout(() => {
          this.RUdoAlert = false;
        }, 2000);
      }
      window.localStorage.setItem(this.mediaName, this.reqF.content);
      try {
        await api.post(srtFullPath, this.reqF.content, true);
        window.localStorage.removeItem(this.srtNotUpload);
      } catch (error) {
        window.localStorage.setItem(this.srtNotUpload, "1");
      }
    },

    async changeRedo() {
      this.onRUdo = true;
      setTimeout(() => {
        this.onRUdo = false;
      }, 1000);
      var srtFullPath = "";
      const path = url.removeLastDir(this.$route.path);
      if (this.onRevision) srtFullPath = this.srtRevisePath;
      else srtFullPath = path + "/" + this.reqF.name;
      this.reqF.content = this.changeNew[this.historyIndex];
      this.historyIndex = this.historyIndex + 1;
      this.cleanUp1();
      this.cleanUp2();
      if (this.isFav) {
        this.switchIsFav();
        setTimeout(() => {
          this.switchIsFav();
        }, 10);
      }
      this.refresh();
      if (!this.RUdoAlert) {
        this.RUdoAlert = true;
        setTimeout(() => {
          this.RUdoAlert = false;
        }, 2000);
      }
      window.localStorage.setItem(this.mediaName, this.reqF.content);
      try {
        await api.post(srtFullPath, this.reqF.content, true);
        window.localStorage.removeItem(this.srtNotUpload);
      } catch (error) {
        window.localStorage.setItem(this.srtNotUpload, "1");
      }
    },

    refresh() {
      if (this.sentenceIndex == 1) {
        this.sentenceIndex = this.sentenceIndex + 1;
        setTimeout(() => {
          this.sentenceIndex = this.sentenceIndex - 1;
        }, 10);
      } else if (this.sentenceIndex == this.srtSubtitles.length + 1) {
        this.sentenceIndex = this.sentenceIndex - 2;
        setTimeout(() => {
          this.sentenceIndex = this.sentenceIndex + 1;
        }, 10);
      } else {
        this.sentenceIndex = this.sentenceIndex - 1;
        setTimeout(() => {
          this.sentenceIndex = this.sentenceIndex + 1;
        }, 10);
      }
    },

    switchRevisePlan() {
      this.cleanUp1();
      this.cleanUp2();
      if (this.onRevision) {
        this.sentenceIndex = this.tempSentenceIndex;
        this.onRevision = false;
        this.showRevision = true;
        this.reqF.content = window.localStorage.getItem(this.mediaName);
        return;
      }
      this.showRevision = !this.showRevision;
      if (this.showRevision) {
        if (this.sentenceIndex > 1) this.indexE = this.sentenceIndex;
        var tempData = this.reviseData;
        for (var i = 0; i < this.reviseData.length; ++i) {
          for (var ii = 0; ii < this.reviseData[i].date.length; ++ii) {
            let afterToday = this.compareDates(
              this.reviseData[i].date[ii].split("^^")[0]
            );
            if (afterToday == 0) {
              tempData[i].date[ii] = tempData[i].date[ii].replace(
                tempData[i].date[ii].split("**")[0],
                tempData[i].date[ii].split("^^")[0] + "^^" + afterToday
              );
            }
          }
        }
        this.reviseData = tempData;
        this.save();
      }
    },
    switchDateReviseStatus(index, i) {
      this.$set(
        this.reviseData[index],
        this.reviseData[index].date[i],
        this.reviseData[index].date[i].split("**")[0] + "**0"
      );
      this.reviseData[index].date[i] =
        this.reviseData[index].date[i].split("**")[0] + "**0";
      this.save();
      this.markRevised = true;
      setTimeout(() => {
        this.markRevised = false;
      }, 1500);
    },

    delRevision(index) {
      this.cleanUp1();
      this.cleanUp2();

      var userConfirmationDelete = window.confirm(
        this.$t("repeater.delThisRevision2")
      );
      if (userConfirmationDelete) {
        this.reviseData.splice(index, 1);
      } else {
        return;
      }
    },

    async updatePreview() {
      if (window.speechSynthesis) window.speechSynthesis.cancel();

      let dirs = this.$route.fullPath.split("/");
      this.name = decodeURIComponent(dirs[dirs.length - 1]);

      if (!this.listing) {
        try {
          const path = url.removeLastDir(this.$route.path);
          const res = await api.fetch(path);
          this.listing = res.items;
        } catch (e) {
          this.$showError(e);
        }
      }
    },

    key(event) {
      if (!this.isReadyToPlay) return;
      if (
        event.which === 39 &&
        this.sentenceIndex < this.srtSubtitles.length &&
        !this.isSetting
      ) {
        // right arrow
        if (
          (document.getElementById("editArea0") &&
            document
              .getElementById("editArea0")
              .contains(document.activeElement)) ||
          (document.getElementById("editArea00") &&
            document
              .getElementById("editArea00")
              .contains(document.activeElement)) ||
          (document.getElementById("editArea1") &&
            document
              .getElementById("editArea1")
              .contains(document.activeElement)) ||
          (document.getElementById("editArea2") &&
            document
              .getElementById("editArea2")
              .contains(document.activeElement)) ||
          (document.getElementById("editArea3") &&
            document
              .getElementById("editArea3")
              .contains(document.activeElement))
        )
          return;
        this.cleanUp2();
        this.cleanUp1();
        this.sentenceIndex = this.sentenceIndex + 1;
        if (
          (this.isSingle && !this.autoPlay) ||
          (this.isFavOnPlay && this.isPlayFullFavList)
        )
          return;
        if (this.isSingle) {
          if (this.isFirstClick) return;
          this.click();
        } else {
          setTimeout(() => {
            this.regularPlay();
            this.currentMedia.currentTime =
              this.srtSubtitles[this.sentenceIndex - 1].startTime;
          }, 1);
        }
        return;
      } else if (
        event.which === 37 &&
        this.sentenceIndex > 1 &&
        !this.isSetting
      ) {
        // left arrow
        if (
          (document.getElementById("editArea0") &&
            document
              .getElementById("editArea0")
              .contains(document.activeElement)) ||
          (document.getElementById("editArea00") &&
            document
              .getElementById("editArea00")
              .contains(document.activeElement)) ||
          (document.getElementById("editArea1") &&
            document
              .getElementById("editArea1")
              .contains(document.activeElement)) ||
          (document.getElementById("editArea2") &&
            document
              .getElementById("editArea2")
              .contains(document.activeElement)) ||
          (document.getElementById("editArea3") &&
            document
              .getElementById("editArea3")
              .contains(document.activeElement))
        )
          return;
        this.cleanUp2();
        this.cleanUp1();
        this.sentenceIndex = this.sentenceIndex - 1;
        if (
          (this.isSingle && !this.autoPlay) ||
          (this.isFavOnPlay && this.isPlayFullFavList)
        )
          return;
        if (this.isSingle) {
          if (this.isFirstClick) return;
          this.click();
        } else {
          setTimeout(() => {
            this.regularPlay();
            this.currentMedia.currentTime =
              this.srtSubtitles[this.sentenceIndex - 1].startTime;
          }, 1);
        }
        return;
      } else if (event.which === 38) {
        // up arrow
        this.cleanUp1(); //stop play
      } else if (event.which === 40 && !this.isSetting) {
        // down arrow
        this.cleanUp1();
        if (this.isFirstClick) this.firstClick();
        if (this.isSingle) {
          setTimeout(() => {
            if (this.pauseAfterFirstDone) {
              this.pauseAfterFirstDone = false;
              if (this.isUtterTransLineFirstly) this.loopPlay();
              else this.utterTransLine();
            } else {
              this.click();
            }
          }, 1);
        } else {
          setTimeout(() => {
            this.regularPlay();
            this.currentMedia.currentTime =
              this.srtSubtitles[this.sentenceIndex - 1].startTime;
          }, 1);
        }
      } else if (event.which === 27) {
        // esc
        this.close();
      }
    },
    handleResize() {
      this.isLandscape = this.checkLandscape();
      this.resized = true;
    },
    close() {
      if (this.isSetting) {
        this.onSetting();
        return;
      }
      if (this.isEditSubandNotes) {
        this.switchEditSubandNote();
        return;
      }
      if (this.isFavOnPlay) {
        this.playFavList();
        return;
      }
      if (this.showSubtitleList || this.showNewWordList || this.withTrans) {
        this.showSubtitleList = false;
        if (this.showNewWordList) {
          if (this.newWordList.length > 0)
            this.newWordList[this.indexOfNewWordList].showTrans = false;
          this.showNewWordList = false;
        }
        this.withTrans = false;
        return;
      }
      if (this.showRevision) {
        this.switchRevisePlan();
        return;
      }
      if (this.onRevision) {
        this.cleanUp1();
        this.cleanUp2();
        this.sentenceIndex = this.tempSentenceIndex;
        this.onRevision = false;
        this.showRevision = true;
        this.reqF.content = window.localStorage.getItem(this.mediaName);
        return;
      }
      this.cleanUp1();
      this.$store.commit("updateRequest", {});
      let uri = url.removeLastDir(this.$route.path) + "/";
      if (this.resized) {
        Vue.nextTick(() => {
          location.assign(uri);
        });
      }
      history.back();
    },
  },
};
</script>

<style>
#repeater {
  background-color: rgba(0, 0, 0, 0.99);
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 1005;
  overflow: hidden;
}
#repeater .repeater {
  text-align: center;
  height: 100%;
  margin: 0 1em 0 1em;
  display: flex;
  flex-direction: column;
  justify-content: space-evenly;
}
#repeater .repeater audio {
  width: 100%;
  left: 0;
  right: 0;
  margin: auto;
}
#repeater .spinner {
  text-align: center;
  position: fixed;
  top: calc(50% + 1.85em);
  left: 50%;
  transform: translate(-50%, -50%);
}
#repeater .spinner > div {
  width: 18px;
  height: 18px;
  background-color: white;
}
input.input.input--repeater {
  margin-bottom: 0.5em;
  display: inline;
  width: 8em;
}
span.subject {
  margin-bottom: 0.5em;
  display: inline-block;
  width: 15em;
  color: white;
}

span.headSubject {
  display: inline;
  color: black;
}

header {
  background: transparent;
}

#settingBoxContainer {
  display: flex;
  position: fixed;
  width: 65%;
  left: 50%;
  transform: translate(-50%, 0);
  top: 4.2em;
  bottom: 0.2em;
  justify-content: center;
  align-items: center;
  z-index: 1010;
}

#settingBox {
  position: relative;
  width: 100%;
  height: 100%;
  padding: 1em;
  border-radius: 10px;
  overflow-y: auto;
  background: grey;
}

.showMsg {
  z-index: 1011;
  position: fixed;
  text-align: center;
  width: 100%;
  left: 50%;
  transform: translate(-50%, 0);
}

input:disabled {
  background-color: #bbbaba;
}

@media (max-width: 736px) {
  #repeater .repeater {
    margin: 0;
  }
  header {
    display: flex;
    justify-content: space-around !important;
  }
  span.subject {
    width: 13em;
  }
  #settingBoxContainer {
    width: 100%;
  }
  input.input.input--repeater {
    margin-bottom: 0.5em;
    display: inline;
    width: 5em;
    padding: 0;
    margin: 0;
  }
}
</style>



评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值