Browse Source

Merge branch 'dev' of github.com:allanpk716/ChineseSubFinder into dev

allan716 3 years ago
parent
commit
988ac8c83b

+ 83 - 7
frontend/package-lock.json

@@ -10,11 +10,13 @@
       "dependencies": {
         "@quasar/extras": "^1.0.0",
         "@vueuse/core": "^4.11.2",
+        "artplayer": "^4.5.8",
         "axios": "^0.25.0",
         "core-js": "^3.6.5",
         "dayjs": "^1.10.8",
         "events": "^3.3.0",
         "github-markdown-css": "^5.1.0",
+        "hls.js": "^0.14.7",
         "markdown": "^0.5.0",
         "quasar": "^2.0.0-beta.1",
         "util": "^0.12.4",
@@ -3375,6 +3377,15 @@
         "node": ">=8"
       }
     },
+    "node_modules/artplayer": {
+      "version": "4.5.8",
+      "resolved": "https://registry.npmjs.org/artplayer/-/artplayer-4.5.8.tgz",
+      "integrity": "sha512-lNII1V38NfLsTn5Z678TObmp3KIHdbZR/1uBzr/+GSfMZb+607yW/xe4vpJN3rX6qjCN6eZpVMNcz7kfH6Db8w==",
+      "dependencies": {
+        "option-validator": "^2.0.6",
+        "screenfull": "^6.0.2"
+      }
+    },
     "node_modules/ast-types-flow": {
       "version": "0.0.7",
       "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
@@ -6133,8 +6144,7 @@
     "node_modules/eventemitter3": {
       "version": "4.0.7",
       "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
-      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
-      "dev": true
+      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
     },
     "node_modules/events": {
       "version": "3.3.0",
@@ -6851,6 +6861,15 @@
         "he": "bin/he"
       }
     },
+    "node_modules/hls.js": {
+      "version": "0.14.17",
+      "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-0.14.17.tgz",
+      "integrity": "sha512-25A7+m6qqp6UVkuzUQ//VVh2EEOPYlOBg32ypr34bcPO7liBMOkKFvbjbCBfiPAOTA/7BSx1Dujft3Th57WyFg==",
+      "dependencies": {
+        "eventemitter3": "^4.0.3",
+        "url-toolkit": "^2.1.6"
+      }
+    },
     "node_modules/hpack.js": {
       "version": "2.1.6",
       "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
@@ -7930,7 +7949,6 @@
       "version": "6.0.3",
       "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
       "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
-      "dev": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -9111,6 +9129,14 @@
         "opener": "bin/opener-bin.js"
       }
     },
+    "node_modules/option-validator": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/option-validator/-/option-validator-2.0.6.tgz",
+      "integrity": "sha512-tmZDan2LRIRQyhUGvkff68/O0R8UmF+Btmiiz0SmSw2ng3CfPZB9wJlIjHpe/MKUZqyIZkVIXCrwr1tIN+0Dzg==",
+      "dependencies": {
+        "kind-of": "^6.0.3"
+      }
+    },
     "node_modules/optionator": {
       "version": "0.9.1",
       "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
@@ -10876,6 +10902,17 @@
         "url": "https://opencollective.com/webpack"
       }
     },
+    "node_modules/screenfull": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-6.0.2.tgz",
+      "integrity": "sha512-AQdy8s4WhNvUZ6P8F6PB21tSPIYKniic+Ogx0AacBMjKP1GUHN2E9URxQHtCusiwxudnCKkdy4GrHXPPJSkCCw==",
+      "engines": {
+        "node": "^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/select-hose": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -12150,6 +12187,11 @@
         "url": "https://opencollective.com/webpack"
       }
     },
+    "node_modules/url-toolkit": {
+      "version": "2.2.5",
+      "resolved": "https://registry.npmjs.org/url-toolkit/-/url-toolkit-2.2.5.tgz",
+      "integrity": "sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg=="
+    },
     "node_modules/url/node_modules/punycode": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
@@ -15939,6 +15981,15 @@
       "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
       "dev": true
     },
+    "artplayer": {
+      "version": "4.5.8",
+      "resolved": "https://registry.npmjs.org/artplayer/-/artplayer-4.5.8.tgz",
+      "integrity": "sha512-lNII1V38NfLsTn5Z678TObmp3KIHdbZR/1uBzr/+GSfMZb+607yW/xe4vpJN3rX6qjCN6eZpVMNcz7kfH6Db8w==",
+      "requires": {
+        "option-validator": "^2.0.6",
+        "screenfull": "^6.0.2"
+      }
+    },
     "ast-types-flow": {
       "version": "0.0.7",
       "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
@@ -18017,8 +18068,7 @@
     "eventemitter3": {
       "version": "4.0.7",
       "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
-      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
-      "dev": true
+      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
     },
     "events": {
       "version": "3.3.0",
@@ -18559,6 +18609,15 @@
       "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
       "dev": true
     },
+    "hls.js": {
+      "version": "0.14.17",
+      "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-0.14.17.tgz",
+      "integrity": "sha512-25A7+m6qqp6UVkuzUQ//VVh2EEOPYlOBg32ypr34bcPO7liBMOkKFvbjbCBfiPAOTA/7BSx1Dujft3Th57WyFg==",
+      "requires": {
+        "eventemitter3": "^4.0.3",
+        "url-toolkit": "^2.1.6"
+      }
+    },
     "hpack.js": {
       "version": "2.1.6",
       "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
@@ -19344,8 +19403,7 @@
     "kind-of": {
       "version": "6.0.3",
       "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
-      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
-      "dev": true
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="
     },
     "klona": {
       "version": "2.0.5",
@@ -20260,6 +20318,14 @@
       "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
       "dev": true
     },
+    "option-validator": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/option-validator/-/option-validator-2.0.6.tgz",
+      "integrity": "sha512-tmZDan2LRIRQyhUGvkff68/O0R8UmF+Btmiiz0SmSw2ng3CfPZB9wJlIjHpe/MKUZqyIZkVIXCrwr1tIN+0Dzg==",
+      "requires": {
+        "kind-of": "^6.0.3"
+      }
+    },
     "optionator": {
       "version": "0.9.1",
       "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
@@ -21492,6 +21558,11 @@
         "ajv-keywords": "^3.5.2"
       }
     },
+    "screenfull": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-6.0.2.tgz",
+      "integrity": "sha512-AQdy8s4WhNvUZ6P8F6PB21tSPIYKniic+Ogx0AacBMjKP1GUHN2E9URxQHtCusiwxudnCKkdy4GrHXPPJSkCCw=="
+    },
     "select-hose": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -22482,6 +22553,11 @@
         }
       }
     },
+    "url-toolkit": {
+      "version": "2.2.5",
+      "resolved": "https://registry.npmjs.org/url-toolkit/-/url-toolkit-2.2.5.tgz",
+      "integrity": "sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg=="
+    },
     "util": {
       "version": "0.12.4",
       "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz",

+ 2 - 0
frontend/package.json

@@ -22,6 +22,8 @@
   "dependencies": {
     "@quasar/extras": "^1.0.0",
     "@vueuse/core": "^4.11.2",
+    "artplayer": "^4.5.8",
+    "hls.js": "^0.14.7",
     "axios": "^0.25.0",
     "core-js": "^3.6.5",
     "dayjs": "^1.10.8",

+ 12 - 0
frontend/src/api/LibraryApi.js

@@ -33,5 +33,17 @@ class LibraryApi extends BaseApi {
   refreshMediaServerSubList = () => this.http(`/v1/subtitles/refresh_media_server_sub_list`, {}, 'POST');
 
   getSubTitleQueueList = () => this.http(`/v1/subtitles/list_manual_upload_2_local_job`);
+
+  addPreviewJob = (data) => this.http(`/v1/preview/add`, data, 'POST');
+
+  getPreviewJobs = () => this.http(`/v1/preview/list`);
+
+  checkIsPreviewInQueue = (data) => this.http(`/v1/preview/is_in_queue`, data, 'POST');
+
+  getPreviewJobResult = (data) => this.http(`/v1/preview/job_result`, data, 'POST');
+
+  getPreviewDistInfo = (data) => this.http(`/v1/preview/export_info`, data, 'POST');
+
+  cleanAllPreviewJobData = () => this.http(`/v1/preview/clean_up`, {}, 'POST');
 }
 export default new LibraryApi();

+ 40 - 0
frontend/src/components/Artplayer.vue

@@ -0,0 +1,40 @@
+<template><div ref="artRef"></div></template>
+<script setup>
+import Artplayer from 'artplayer';
+import { nextTick, onBeforeUnmount, onMounted, ref, watchEffect } from 'vue';
+
+const props = defineProps({
+  option: { type: Object, required: true },
+});
+
+const emit = defineEmits(['get-instance']);
+
+const instance = ref(null);
+const artRef = ref(null);
+
+const setup = () => {
+  instance.value = new Artplayer({
+    ...props.option,
+    container: artRef.value,
+  });
+  nextTick(() => {
+    emit('get-instance', instance.value);
+  });
+};
+
+watchEffect(
+  () => props.option,
+  () => {
+    instance.value?.destroy();
+    setup();
+  }
+);
+
+onMounted(() => {
+  setup();
+});
+
+onBeforeUnmount(() => {
+  instance.value?.destroy();
+});
+</script>

+ 205 - 0
frontend/src/pages/library/BtnDialogPreviewVideo.vue

@@ -0,0 +1,205 @@
+<template>
+  <q-btn color="primary" icon="smart_display" flat dense v-bind="$attrs" @click="visible = true" title="预览" />
+
+  <q-dialog
+    v-model="visible"
+    persistent
+    transition-show="slide-up"
+    transition-hide="slide-down"
+    maximized
+    @before-show="handleBeforeShow"
+    @before-hide="handleBeforeHide"
+  >
+    <q-card class="column">
+      <q-bar>
+        <div class="text-bold">字幕预览</div>
+        <q-space />
+        <q-btn dense flat icon="close" v-close-popup title="关闭" />
+      </q-bar>
+
+      <q-card-section class="col row items-center justify-center">
+        <div class="row q-pa-md justify-center" v-if="isJobSuccess">
+          <artplayer :option="artOption" style="height: 80vh; width: calc(1920 / 1080 * 80vh)"></artplayer>
+        </div>
+        <div v-else-if="isJobFailed || isJobNotExists" class="column items-center">
+          <div class="text-grey-4" style="font-size: 120px; letter-spacing: 10px">(;-;)</div>
+          <div class="text-negative q-mt-md text-bold">
+            {{ jobResult.message || '未知错误' }}
+          </div>
+          <div class="q-mt-md">
+            <q-btn label="点我重试" @click="preview" outline color="primary" />
+          </div>
+        </div>
+        <div v-else class="column items-center">
+          <q-spinner-cube size="xl" color="primary" />
+          <div class="q-mt-sm text-grey-8">视频转码中...</div>
+        </div>
+      </q-card-section>
+    </q-card>
+  </q-dialog>
+</template>
+
+<script setup>
+import { computed, ref } from 'vue';
+import Hls from 'hls.js';
+import LibraryApi from 'src/api/LibraryApi';
+import { SystemMessage } from 'src/utils/Message';
+import useInterval from 'src/composables/useInterval';
+import { until } from '@vueuse/core';
+import Artplayer from 'components/Artplayer';
+import config from 'src/config';
+import { useQuasar } from 'quasar';
+
+const $q = useQuasar();
+
+const START_TIME = '0';
+const END_TIME = '300';
+
+const getPreviewUrl = (url) => `${config.BACKEND_URL}/static/preview/${url}`;
+
+const props = defineProps({
+  path: String,
+  subList: {
+    type: Array,
+    default: () => [],
+  },
+});
+
+const visible = ref(false);
+const loading = ref(false);
+
+const selectedSub = ref(null);
+const previewInfo = ref(null);
+const jobResult = ref(null);
+
+const isJobSuccess = computed(() => jobResult.value?.message === 'ok');
+const isJobNotExists = computed(() => jobResult.value?.message === '');
+const isJobFailed = computed(() => jobResult.value?.message && !isJobSuccess.value && !isJobNotExists.value);
+
+const submitForm = computed(() => ({
+  video_f_path: props.path,
+  sub_f_path: selectedSub.value,
+  start_time: START_TIME,
+  end_time: END_TIME,
+}));
+
+const getIsInQueue = async () => {
+  const [res] = await LibraryApi.checkIsPreviewInQueue(submitForm.value);
+
+  return res?.message === 'true';
+};
+
+const { resetInterval: startCheckQueue, stopInterval: stopCheckQueue } = useInterval(
+  async () => {
+    loading.value = await getIsInQueue();
+  },
+  5000,
+  false
+);
+
+const checkQueue = async () => {
+  loading.value = true;
+  startCheckQueue();
+  await until(loading).toBe(false);
+  stopCheckQueue();
+};
+
+const getPreviewInfo = async () => {
+  const [res, err1] = await LibraryApi.getPreviewDistInfo(submitForm.value);
+  if (err1 !== null) {
+    SystemMessage.error(err1.message);
+  } else {
+    previewInfo.value = res;
+  }
+};
+
+const preview = async () => {
+  previewInfo.value = null;
+  const isInQueue = await getIsInQueue();
+  if (!isInQueue) {
+    jobResult.value = null;
+    const [, err] = await LibraryApi.addPreviewJob(submitForm.value);
+    if (err !== null) {
+      SystemMessage.error(err.message);
+    } else {
+      // 等待预览任务完成
+      await checkQueue();
+      const [res1] = await LibraryApi.getPreviewJobResult(submitForm.value);
+      jobResult.value = res1;
+    }
+  }
+  loading.value = false;
+};
+
+const artOption = computed(() => ({
+  autoplay: true,
+  autoSize: true,
+  url: getPreviewUrl(previewInfo.value.video_f_path),
+  subtitle: {
+    url: getPreviewUrl(previewInfo.value.sub_f_path),
+  },
+  customType: {
+    m3u8(video, url) {
+      if (Hls.isSupported()) {
+        const hls = new Hls();
+        hls.loadSource(url);
+        hls.attachMedia(video);
+      } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
+        video.src = url;
+      } else {
+        // art.notice.show = '不支持播放格式:m3u8';
+      }
+    },
+  },
+  controls:
+    props.subList.length === 0
+      ? []
+      : [
+          {
+            disable: false,
+            name: 'button',
+            index: 10,
+            position: 'right',
+            html: '选择字幕',
+            tooltip: '选择字幕',
+            style: {
+              color: 'red',
+            },
+            click() {
+              $q.dialog({
+                title: '选择字幕',
+                style: 'width: 800px',
+                options: {
+                  type: 'radio',
+                  model: selectedSub.value,
+                  items: props.subList.map((e) => ({ label: e, value: e })),
+                },
+                cancel: true,
+                persistent: true,
+              }).onOk((data) => {
+                selectedSub.value = data;
+                getPreviewInfo();
+                preview();
+              });
+            },
+            mounted() {
+              // console.log('自定义按钮挂载完成1');
+            },
+          },
+        ],
+}));
+
+const handleBeforeShow = () => {
+  loading.value = true;
+  selectedSub.value = props.subList?.[0];
+  getPreviewInfo();
+  preview();
+};
+
+const handleBeforeHide = () => {
+  stopCheckQueue();
+  previewInfo.value = null;
+  jobResult.value = null;
+  LibraryApi.cleanAllPreviewJobData();
+};
+</script>

+ 2 - 0
frontend/src/pages/library/BtnIgnoreVideo.vue

@@ -9,6 +9,7 @@
     @click.stop
     title="当前视频已锁定,不进行字幕下载"
     @click="skip"
+    v-bind="$attrs"
   />
   <q-btn
     v-else
@@ -20,6 +21,7 @@
     @click.stop
     title="点击锁定视频,不进行字幕下载"
     @click="skip"
+    v-bind="$attrs"
   />
 </template>
 

+ 1 - 0
frontend/src/pages/library/BtnUploadSubtitle.vue

@@ -9,6 +9,7 @@
     flat
     dense
     icon="upload"
+    v-bind="$attrs"
     :label="dense ? '' : '上传本地字幕'"
     @click="handleUploadClick"
     title="上传本地字幕"

+ 23 - 5
frontend/src/pages/library/movies/ListItemMovie.vue

@@ -13,8 +13,25 @@
     </div>
     <div class="content-width text-ellipsis-line-2" :title="data.name">{{ data.name }}</div>
     <div class="row items-center">
+      <btn-dialog-preview-video
+        v-if="hasSubtitle"
+        size="sm"
+        :sub-list="detialInfo?.sub_f_path_list"
+        :path="data.video_f_path"
+      />
+
       <div>
-        <q-btn v-if="hasSubtitle" color="black" round flat dense icon="closed_caption" @click.stop title="已有字幕">
+        <q-btn
+          v-if="hasSubtitle"
+          size="sm"
+          color="black"
+          round
+          flat
+          dense
+          icon="closed_caption"
+          @click.stop
+          title="已有字幕"
+        >
           <q-popup-proxy>
             <q-list dense>
               <q-item v-for="(item, index) in detialInfo.sub_url_list" :key="item">
@@ -27,12 +44,11 @@
             </q-list>
           </q-popup-proxy>
         </q-btn>
-        <q-btn v-else color="grey" round flat dense icon="closed_caption" @click.stop title="没有字幕" />
+        <q-btn v-else color="grey" size="sm" round flat dense icon="closed_caption" @click.stop title="没有字幕" />
       </div>
-
       <q-space />
 
-      <btn-upload-subtitle :path="data.video_f_path" dense />
+      <btn-upload-subtitle :path="data.video_f_path" dense size="sm" />
 
       <q-btn
         class="btn-download"
@@ -43,10 +59,11 @@
         icon="download_for_offline"
         title="下载字幕"
         @click="downloadSubtitle"
+        size="sm"
       ></q-btn>
 
       <div>
-        <btn-ignore-video :path="props.data.video_f_path" :video-type="VIDEO_TYPE_MOVIE" />
+        <btn-ignore-video :path="props.data.video_f_path" :video-type="VIDEO_TYPE_MOVIE" size="sm" />
       </div>
     </div>
   </q-card>
@@ -61,6 +78,7 @@ import { useQuasar } from 'quasar';
 import { getUrl, subtitleUploadList } from 'pages/library/useLibrary';
 import BtnIgnoreVideo from 'pages/library/BtnIgnoreVideo';
 import BtnUploadSubtitle from 'pages/library/BtnUploadSubtitle';
+import BtnDialogPreviewVideo from 'pages/library/BtnDialogPreviewVideo';
 
 const props = defineProps({
   data: Object,

+ 8 - 0
frontend/src/pages/library/tvs/DialogTVDetail.vue

@@ -68,12 +68,19 @@
                   <q-checkbox v-model="selection" :val="item" />
                 </q-item-section>
                 <q-item-section>第 {{ pandStart2(item.episode) }} 集</q-item-section>
+
+                <q-item-section v-if="item.sub_f_path_list.length" side>
+                  <btn-dialog-preview-video :sub-list="item.sub_f_path_list" :path="item.video_f_path" />
+                </q-item-section>
+
                 <q-item-section side>
                   <btn-upload-subtitle :path="item.video_f_path" />
                 </q-item-section>
+
                 <q-item-section side>
                   <btn-ignore-video :path="item.video_f_path" :video-type="VIDEO_TYPE_TV" />
                 </q-item-section>
+
                 <q-item-section side>
                   <q-btn
                     v-if="item.sub_f_path_list.length"
@@ -134,6 +141,7 @@ import { useSelection } from 'src/composables/useSelection';
 import BtnIgnoreVideo from 'pages/library/BtnIgnoreVideo';
 import eventBus from 'vue3-eventbus';
 import BtnUploadSubtitle from 'pages/library/BtnUploadSubtitle';
+import BtnDialogPreviewVideo from 'pages/library/BtnDialogPreviewVideo';
 
 const props = defineProps({
   data: Object,

+ 0 - 1
frontend/src/pages/settings/BtnCheckTmdbApi.vue

@@ -12,7 +12,6 @@ const loading = ref(false);
 
 const check = async () => {
   loading.value = true;
-  console.log(formModel);
   const [res, err] = await CommonApi.checkTmdbApiKey({
     proxy_settings: formModel.advanced_settings.proxy_settings,
     api_key: formModel.advanced_settings.tmdb_api_settings.api_key,