mirror of
https://github.com/EstrellaXD/Auto_Bangumi.git
synced 2026-04-13 18:11:03 +08:00
番剧订阅
This commit is contained in:
20
src/App.vue
20
src/App.vue
@@ -1,12 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import { NMessageProvider } from 'naive-ui';
|
||||
import {
|
||||
NMessageProvider,
|
||||
NConfigProvider,
|
||||
type GlobalThemeOverrides,
|
||||
} from 'naive-ui';
|
||||
|
||||
const theme: GlobalThemeOverrides = {
|
||||
Spin: {
|
||||
color: '#fff',
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Suspense>
|
||||
<NMessageProvider>
|
||||
<RouterView></RouterView>
|
||||
</NMessageProvider>
|
||||
<n-config-provider :theme-overrides="theme">
|
||||
<n-message-provider>
|
||||
<RouterView></RouterView>
|
||||
</n-message-provider>
|
||||
</n-config-provider>
|
||||
</Suspense>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import type { BangumiRule } from '#/bangumi';
|
||||
|
||||
interface Status {
|
||||
status: 'Success' | 'Failed to parse link';
|
||||
status: 'Success';
|
||||
}
|
||||
|
||||
interface AnalysisError {
|
||||
status: 'Failed to parse link';
|
||||
}
|
||||
|
||||
export const apiDownload = {
|
||||
@@ -10,10 +14,25 @@ export const apiDownload = {
|
||||
* @param rss_link - RSS 链接
|
||||
*/
|
||||
async analysis(rss_link: string) {
|
||||
const { data } = await axios.post<BangumiRule>('api/v1/download/analysis', {
|
||||
rss_link,
|
||||
});
|
||||
return data;
|
||||
const fetchResult = createEventHook<BangumiRule>();
|
||||
const fetchError = createEventHook<AnalysisError>();
|
||||
|
||||
axios
|
||||
.post<any>('api/v1/download/analysis', {
|
||||
rss_link,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (data.status) {
|
||||
fetchError.trigger(data as AnalysisError);
|
||||
} else {
|
||||
fetchResult.trigger(data as BangumiRule);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
onResult: fetchResult.on,
|
||||
onError: fetchError.on,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
<script lang="ts" setup>
|
||||
import { NSpin } from 'naive-ui';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
type?: 'primary' | 'warn';
|
||||
size?: 'big' | 'normal' | 'small';
|
||||
link?: string | null;
|
||||
loading?: boolean;
|
||||
}>(),
|
||||
{
|
||||
type: 'primary',
|
||||
size: 'normal',
|
||||
link: null,
|
||||
loading: false,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -24,6 +28,17 @@ const buttonSize = computed(() => {
|
||||
return 'rounded-6px w-86px h-28px text-main';
|
||||
}
|
||||
});
|
||||
|
||||
const loadingSize = computed(() => {
|
||||
switch (props.size) {
|
||||
case 'big':
|
||||
return 'large';
|
||||
case 'normal':
|
||||
return 'small';
|
||||
case 'small':
|
||||
return 18;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -36,7 +51,9 @@ const buttonSize = computed(() => {
|
||||
:class="[`type-${type}`, buttonSize]"
|
||||
@click="$emit('click')"
|
||||
>
|
||||
<slot></slot>
|
||||
<n-spin :show="loading" :size="loadingSize">
|
||||
<slot></slot>
|
||||
</n-spin>
|
||||
</Component>
|
||||
</template>
|
||||
|
||||
|
||||
2
src/components.d.ts
vendored
2
src/components.d.ts
vendored
@@ -10,6 +10,7 @@ export {}
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
AbAdd: typeof import('./basic/ab-add.vue')['default']
|
||||
AbAddBangumi: typeof import('./components/ab-add-bangumi.vue')['default']
|
||||
AbBangumiCard: typeof import('./components/ab-bangumi-card.vue')['default']
|
||||
AbButton: typeof import('./basic/ab-button.vue')['default']
|
||||
AbChangeAccount: typeof import('./components/ab-change-account.vue')['default']
|
||||
@@ -21,6 +22,7 @@ declare module '@vue/runtime-core' {
|
||||
AbLabel: typeof import('./components/ab-label.vue')['default']
|
||||
AbPageTitle: typeof import('./basic/ab-page-title.vue')['default']
|
||||
AbPopup: typeof import('./components/ab-popup.vue')['default']
|
||||
AbRule: typeof import('./components/ab-rule.vue')['default']
|
||||
AbSearch: typeof import('./basic/ab-search.vue')['default']
|
||||
AbSelect: typeof import('./basic/ab-select.vue')['default']
|
||||
AbSetting: typeof import('./components/ab-setting.vue')['default']
|
||||
|
||||
126
src/components/ab-add-bangumi.vue
Normal file
126
src/components/ab-add-bangumi.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<script lang="ts" setup>
|
||||
import { useMessage } from 'naive-ui';
|
||||
import type { BangumiRule } from '#/bangumi';
|
||||
|
||||
const { getAll } = useBangumiStore();
|
||||
const show = defineModel('show', { default: false });
|
||||
|
||||
const rss = ref('');
|
||||
const message = useMessage();
|
||||
const rule = ref<BangumiRule>();
|
||||
const analysis = reactive({
|
||||
loading: false,
|
||||
next: false,
|
||||
});
|
||||
|
||||
const loading = reactive({
|
||||
collect: false,
|
||||
subscribe: false,
|
||||
});
|
||||
|
||||
watch(show, (val) => {
|
||||
if (!val) {
|
||||
rss.value = '';
|
||||
setTimeout(() => {
|
||||
analysis.next = false;
|
||||
}, 300);
|
||||
}
|
||||
});
|
||||
|
||||
const analyser = async () => {
|
||||
if (rss.value === '') {
|
||||
message.error('Please enter the RSS link!');
|
||||
} else {
|
||||
try {
|
||||
analysis.loading = true;
|
||||
const { onError, onResult } = await apiDownload.analysis(rss.value);
|
||||
onResult((data) => {
|
||||
rule.value = data;
|
||||
analysis.loading = false;
|
||||
analysis.next = true;
|
||||
console.log('rule', data);
|
||||
});
|
||||
|
||||
onError((err) => {
|
||||
message.error(err.status);
|
||||
analysis.loading = false;
|
||||
console.log('error', err);
|
||||
});
|
||||
} catch (error) {
|
||||
message.error('Failed to analyser!');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const collect = async () => {
|
||||
if (rule.value) {
|
||||
try {
|
||||
loading.collect = true;
|
||||
const res = await apiDownload.collection(rule.value);
|
||||
loading.collect = false;
|
||||
if (res) {
|
||||
message.success('Collect Success!');
|
||||
getAll();
|
||||
show.value = false;
|
||||
} else {
|
||||
message.error('Collect Failed!');
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('Collect Error!');
|
||||
}
|
||||
}
|
||||
};
|
||||
const subscribe = async () => {
|
||||
if (rule.value) {
|
||||
try {
|
||||
loading.subscribe = true;
|
||||
const res = await apiDownload.subscribe(rule.value);
|
||||
loading.subscribe = false;
|
||||
if (res) {
|
||||
message.success('Subscribe Success!');
|
||||
getAll();
|
||||
show.value = false;
|
||||
} else {
|
||||
message.error('Subscribe Failed!');
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('Subscribe Error!');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ab-popup v-model:show="show" title="Add Bangumi" css="w-360px">
|
||||
<div space-y-12px v-if="!analysis.next">
|
||||
<ab-setting
|
||||
label="RSS Link"
|
||||
type="input"
|
||||
:prop="{
|
||||
placeholder: 'Please enter the RSS link',
|
||||
}"
|
||||
:bottom-line="true"
|
||||
v-model:data="rss"
|
||||
></ab-setting>
|
||||
|
||||
<div flex="~ justify-end">
|
||||
<ab-button size="small" :loading="analysis.loading" @click="analyser"
|
||||
>Analyser</ab-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<ab-rule v-model:rule="rule"></ab-rule>
|
||||
|
||||
<div flex="~ justify-end" space-x-10px>
|
||||
<ab-button size="small" :loading="loading.collect" @click="collect"
|
||||
>Collect</ab-button
|
||||
>
|
||||
<ab-button size="small" :loading="loading.subscribe" @click="subscribe"
|
||||
>Subscribe</ab-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</ab-popup>
|
||||
</template>
|
||||
@@ -1,23 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import type { AbEditRuleItem, SettingItem } from '#/components';
|
||||
import type { BangumiRule } from '#/bangumi';
|
||||
|
||||
const emit = defineEmits<{
|
||||
delete: [{ id: number; deleteFile: boolean }];
|
||||
apply: [item: AbEditRuleItem];
|
||||
apply: [item: BangumiRule];
|
||||
}>();
|
||||
|
||||
const show = defineModel('show', { default: false });
|
||||
const item = defineModel<AbEditRuleItem>('item', {
|
||||
default: () => {
|
||||
return {
|
||||
id: -1,
|
||||
official_title: '',
|
||||
year: '',
|
||||
season: 1,
|
||||
offset: 0,
|
||||
filter: [],
|
||||
};
|
||||
},
|
||||
const rule = defineModel<BangumiRule>('rule', {
|
||||
required: true,
|
||||
});
|
||||
|
||||
const deleteRuleDialog = ref(false);
|
||||
@@ -29,69 +20,19 @@ watch(show, (val) => {
|
||||
|
||||
function emitDelete(deleteFile: boolean) {
|
||||
emit('delete', {
|
||||
id: item.value.id,
|
||||
id: rule.value.id,
|
||||
deleteFile,
|
||||
});
|
||||
}
|
||||
function emitApply() {
|
||||
emit('apply', item.value);
|
||||
emit('apply', rule.value);
|
||||
}
|
||||
|
||||
const items: SettingItem<AbEditRuleItem>[] = [
|
||||
{
|
||||
configKey: 'official_title',
|
||||
label: 'Officical Ttile',
|
||||
type: 'input',
|
||||
prop: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
{
|
||||
configKey: 'year',
|
||||
label: 'Year',
|
||||
type: 'input',
|
||||
css: 'w-72px',
|
||||
prop: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
{
|
||||
configKey: 'season',
|
||||
label: 'Season',
|
||||
type: 'input',
|
||||
css: 'w-72px',
|
||||
prop: {
|
||||
type: 'number',
|
||||
},
|
||||
bottomLine: true,
|
||||
},
|
||||
{
|
||||
configKey: 'offset',
|
||||
label: 'Offset',
|
||||
type: 'input',
|
||||
css: 'w-72px',
|
||||
prop: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
{
|
||||
configKey: 'filter',
|
||||
label: 'Exclude',
|
||||
type: 'dynamic-tags',
|
||||
bottomLine: true,
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ab-popup v-model:show="show" title="Edit Rule" css="w-380px">
|
||||
<div space-y-12px>
|
||||
<ab-setting
|
||||
v-for="i in items"
|
||||
:key="i.configKey"
|
||||
v-bind="i"
|
||||
v-model:data="item[i.configKey]"
|
||||
></ab-setting>
|
||||
<ab-rule v-model:rule="rule"></ab-rule>
|
||||
|
||||
<div fx-cer justify-end space-x-10px>
|
||||
<ab-button
|
||||
@@ -109,10 +50,10 @@ const items: SettingItem<AbEditRuleItem>[] = [
|
||||
<div line my-8px></div>
|
||||
|
||||
<div fx-cer justify-center space-x-10px>
|
||||
<ab-button size="small" type="warn" @click="emitDelete(true)"
|
||||
<ab-button size="small" type="warn" @click="() => emitDelete(true)"
|
||||
>Yes</ab-button
|
||||
>
|
||||
<ab-button size="small" @click="emitDelete(false)">No</ab-button>
|
||||
<ab-button size="small" @click="() => emitDelete(false)">No</ab-button>
|
||||
</div>
|
||||
</ab-popup>
|
||||
</ab-popup>
|
||||
|
||||
64
src/components/ab-rule.vue
Normal file
64
src/components/ab-rule.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<script lang="ts" setup>
|
||||
import type { BangumiRule } from '#/bangumi';
|
||||
import type { SettingItem } from '#/components';
|
||||
|
||||
const rule = defineModel<BangumiRule>('rule', {
|
||||
required: true,
|
||||
});
|
||||
|
||||
const items: SettingItem<BangumiRule>[] = [
|
||||
{
|
||||
configKey: 'official_title',
|
||||
label: 'Officical Ttile',
|
||||
type: 'input',
|
||||
prop: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
{
|
||||
configKey: 'year',
|
||||
label: 'Year',
|
||||
type: 'input',
|
||||
css: 'w-72px',
|
||||
prop: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
{
|
||||
configKey: 'season',
|
||||
label: 'Season',
|
||||
type: 'input',
|
||||
css: 'w-72px',
|
||||
prop: {
|
||||
type: 'number',
|
||||
},
|
||||
bottomLine: true,
|
||||
},
|
||||
{
|
||||
configKey: 'offset',
|
||||
label: 'Offset',
|
||||
type: 'input',
|
||||
css: 'w-72px',
|
||||
prop: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
{
|
||||
configKey: 'filter',
|
||||
label: 'Exclude',
|
||||
type: 'dynamic-tags',
|
||||
bottomLine: true,
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div space-y-12px>
|
||||
<ab-setting
|
||||
v-for="i in items"
|
||||
:key="i.configKey"
|
||||
v-bind="i"
|
||||
v-model:data="rule[i.configKey]"
|
||||
></ab-setting>
|
||||
</div>
|
||||
</template>
|
||||
@@ -16,13 +16,22 @@ withDefaults(
|
||||
running: false,
|
||||
}
|
||||
);
|
||||
|
||||
defineEmits(['clickAdd']);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Menu>
|
||||
<div rel>
|
||||
<div fx-cer space-x-16px>
|
||||
<AddOne theme="outline" size="24" fill="#fff" is-btn btn-click />
|
||||
<AddOne
|
||||
theme="outline"
|
||||
size="24"
|
||||
fill="#fff"
|
||||
is-btn
|
||||
btn-click
|
||||
@click="() => $emit('clickAdd')"
|
||||
/>
|
||||
|
||||
<MenuButton bg-transparent is-btn btn-click>
|
||||
<More theme="outline" size="24" fill="#fff" />
|
||||
|
||||
@@ -1,35 +1,40 @@
|
||||
<script lang="ts" setup>
|
||||
import type { BangumiRule } from '#/bangumi';
|
||||
import type { AbEditRuleItem } from '#/components';
|
||||
|
||||
const { data } = storeToRefs(useBangumiStore());
|
||||
const { getAll, updateRule, removeRule } = useBangumiStore();
|
||||
|
||||
const editRule = reactive<{
|
||||
show: boolean;
|
||||
item: AbEditRuleItem;
|
||||
item: BangumiRule;
|
||||
}>({
|
||||
show: false,
|
||||
item: {
|
||||
id: -1,
|
||||
official_title: '',
|
||||
year: '',
|
||||
season: 1,
|
||||
offset: 0,
|
||||
added: false,
|
||||
deleted: false,
|
||||
dpi: '',
|
||||
eps_collect: false,
|
||||
filter: [],
|
||||
group_name: '',
|
||||
id: 0,
|
||||
official_title: '',
|
||||
offset: 0,
|
||||
poster_link: '',
|
||||
rss_link: [],
|
||||
rule_name: '',
|
||||
save_path: '',
|
||||
season: 1,
|
||||
season_raw: '',
|
||||
source: null,
|
||||
subtitle: '',
|
||||
title_raw: '',
|
||||
year: null,
|
||||
},
|
||||
});
|
||||
|
||||
function open(data: BangumiRule) {
|
||||
async function open(data: BangumiRule) {
|
||||
editRule.show = true;
|
||||
editRule.item = {
|
||||
id: data.id,
|
||||
official_title: data.official_title,
|
||||
year: data.year ?? '',
|
||||
season: data.season,
|
||||
offset: data.offset,
|
||||
filter: data.filter,
|
||||
};
|
||||
editRule.item = data;
|
||||
}
|
||||
|
||||
async function deleteRule({
|
||||
@@ -46,11 +51,8 @@ async function deleteRule({
|
||||
}
|
||||
}
|
||||
|
||||
async function applyRule(newData: AbEditRuleItem) {
|
||||
const id = newData.id;
|
||||
const oldData = await apiBangumi.getRule(id);
|
||||
const data = Object.assign(oldData, newData);
|
||||
const res = await updateRule(data);
|
||||
async function applyRule(newData: BangumiRule) {
|
||||
const res = await updateRule(newData);
|
||||
if (res) {
|
||||
editRule.show = false;
|
||||
getAll();
|
||||
@@ -75,12 +77,12 @@ definePage({
|
||||
:poster="i.poster_link ?? ''"
|
||||
:name="i.official_title"
|
||||
:season="i.season"
|
||||
@click="open(i)"
|
||||
@click="() => open(i)"
|
||||
></ab-bangumi-card>
|
||||
|
||||
<AbEditRule
|
||||
v-model:show="editRule.show"
|
||||
:item="editRule.item"
|
||||
v-model:rule="editRule.item"
|
||||
@delete="deleteRule"
|
||||
@apply="applyRule"
|
||||
></AbEditRule>
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
.fade {
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
position: absolute;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
&-enter-from,
|
||||
&-leave-to {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
Logout,
|
||||
MenuUnfold,
|
||||
Play,
|
||||
Setting,
|
||||
SettingTwo,
|
||||
} from '@icon-park/vue-next';
|
||||
|
||||
const props = withDefaults(
|
||||
@@ -58,7 +58,7 @@ const items = [
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
icon: Setting,
|
||||
icon: SettingTwo,
|
||||
label: 'Config',
|
||||
path: '/config',
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Me, Pause, PlayOne, Power, Refresh } from '@icon-park/vue-next';
|
||||
|
||||
const search = ref('');
|
||||
const show = ref(false);
|
||||
const showAdd = ref(false);
|
||||
|
||||
const { onUpdate, offUpdate, start, pause, shutdown, restart } =
|
||||
useProgramStore();
|
||||
@@ -59,9 +60,15 @@ onUnmounted(() => {
|
||||
<ab-search v-model:value="search" />
|
||||
|
||||
<div ml-auto>
|
||||
<ab-status-bar :items="items" :running="running"></ab-status-bar>
|
||||
<ab-status-bar
|
||||
:items="items"
|
||||
:running="running"
|
||||
@click-add="() => (showAdd = true)"
|
||||
></ab-status-bar>
|
||||
</div>
|
||||
|
||||
<ab-change-account v-model:show="show"></ab-change-account>
|
||||
|
||||
<ab-add-bangumi v-model:show="showAdd"></ab-add-bangumi>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -16,12 +16,3 @@ export interface AbSettingProps {
|
||||
export type SettingItem<T> = AbSettingProps & {
|
||||
configKey: keyof T;
|
||||
};
|
||||
|
||||
export interface AbEditRuleItem {
|
||||
id: number;
|
||||
official_title: string;
|
||||
year: string;
|
||||
season: number;
|
||||
offset: number;
|
||||
filter: string[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user