mirror of
https://github.com/EstrellaXD/Auto_Bangumi.git
synced 2026-04-03 18:50:11 +08:00
webui: add rss manage function.
This commit is contained in:
@@ -30,6 +30,15 @@ async def add_rss(rss: RSSItem, current_user=Depends(get_current_user)):
|
||||
return u_response(result)
|
||||
|
||||
|
||||
@router.post(path="/enable/many", response_model=APIResponse)
|
||||
async def enable_many_rss(rss_ids: list[int], current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise UNAUTHORIZED
|
||||
with RSSEngine() as engine:
|
||||
result = engine.enable_list(rss_ids)
|
||||
return u_response(result)
|
||||
|
||||
|
||||
@router.delete(path="/delete/{rss_id}", response_model=APIResponse)
|
||||
async def delete_rss(rss_id: int, current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
@@ -47,6 +56,15 @@ async def delete_rss(rss_id: int, current_user=Depends(get_current_user)):
|
||||
)
|
||||
|
||||
|
||||
@router.post(path="/delete/many", response_model=APIResponse)
|
||||
async def delete_many_rss(rss_ids: list[int], current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise UNAUTHORIZED
|
||||
with RSSEngine() as engine:
|
||||
result = engine.delete_list(rss_ids)
|
||||
return u_response(result)
|
||||
|
||||
|
||||
@router.patch(path="/disable/{rss_id}", response_model=APIResponse)
|
||||
async def disable_rss(rss_id: int, current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
@@ -69,16 +87,8 @@ async def disable_many_rss(rss_ids: list[int], current_user=Depends(get_current_
|
||||
if not current_user:
|
||||
raise UNAUTHORIZED
|
||||
with RSSEngine() as engine:
|
||||
if engine.disable_list(rss_ids):
|
||||
return JSONResponse(
|
||||
status_code=200,
|
||||
content={"msg_en": "Disable RSS successfully.", "msg_zh": "禁用 RSS 成功。"},
|
||||
)
|
||||
else:
|
||||
return JSONResponse(
|
||||
status_code=406,
|
||||
content={"msg_en": "Disable RSS failed.", "msg_zh": "禁用 RSS 失败。"},
|
||||
)
|
||||
result = engine.disable_list(rss_ids)
|
||||
return u_response(result)
|
||||
|
||||
|
||||
@router.patch(path="/update/{rss_id}", response_model=APIResponse)
|
||||
|
||||
@@ -44,6 +44,17 @@ class RSSDatabase:
|
||||
self.session.refresh(db_data)
|
||||
return True
|
||||
|
||||
def enable(self, _id: int):
|
||||
statement = select(RSSItem).where(RSSItem.id == _id)
|
||||
db_data = self.session.exec(statement).first()
|
||||
if not db_data:
|
||||
return False
|
||||
db_data.enabled = True
|
||||
self.session.add(db_data)
|
||||
self.session.commit()
|
||||
self.session.refresh(db_data)
|
||||
return True
|
||||
|
||||
def disable(self, _id: int):
|
||||
statement = select(RSSItem).where(RSSItem.id == _id)
|
||||
db_data = self.session.exec(statement).first()
|
||||
|
||||
@@ -62,6 +62,32 @@ class RSSEngine(Database):
|
||||
def disable_list(self, rss_id_list: list[int]):
|
||||
for rss_id in rss_id_list:
|
||||
self.rss.disable(rss_id)
|
||||
return ResponseModel(
|
||||
status=True,
|
||||
status_code=200,
|
||||
msg_en="Disable RSS successfully.",
|
||||
msg_zh="禁用 RSS 成功。",
|
||||
)
|
||||
|
||||
def enable_list(self, rss_id_list: list[int]):
|
||||
for rss_id in rss_id_list:
|
||||
self.rss.enable(rss_id)
|
||||
return ResponseModel(
|
||||
status=True,
|
||||
status_code=200,
|
||||
msg_en="Enable RSS successfully.",
|
||||
msg_zh="启用 RSS 成功。",
|
||||
)
|
||||
|
||||
def delete_list(self, rss_id_list: list[int]):
|
||||
for rss_id in rss_id_list:
|
||||
self.rss.delete(rss_id)
|
||||
return ResponseModel(
|
||||
status=True,
|
||||
status_code=200,
|
||||
msg_en="Delete RSS successfully.",
|
||||
msg_zh="删除 RSS 成功。",
|
||||
)
|
||||
|
||||
def pull_rss(self, rss_item: RSSItem) -> list[Torrent]:
|
||||
torrents = self._get_torrents(rss_item)
|
||||
|
||||
@@ -38,6 +38,11 @@ export const apiRSS = {
|
||||
return data!;
|
||||
},
|
||||
|
||||
async enableMany(rss_list: number[]) {
|
||||
const { data } = await axios.post<ApiSuccess>(`api/v1/rss/enable/many`, rss_list);
|
||||
return data!;
|
||||
},
|
||||
|
||||
async refreshAll() {
|
||||
const { data } = await axios.get<ApiSuccess>('api/v1/rss/refresh/all');
|
||||
return data!;
|
||||
|
||||
@@ -65,6 +65,7 @@ defineEmits(['click']);
|
||||
<div text-h3 truncate>{{ name }}</div>
|
||||
<ab-tag
|
||||
:title="`Season ${season}`"
|
||||
type="primary"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -90,6 +91,7 @@ defineEmits(['click']);
|
||||
<div flex space-x-8px>
|
||||
<ab-tag
|
||||
:title="`Season ${season}`"
|
||||
type="primary"
|
||||
/>
|
||||
<ab-tag
|
||||
v-if="group !== ''"
|
||||
@@ -99,7 +101,7 @@ defineEmits(['click']);
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ab-add round="true" type="medium" @click="()=> $emit('click')"/>
|
||||
<ab-add :round="true" type="medium" @click="()=> $emit('click')"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -10,11 +10,13 @@ withDefaults(
|
||||
parser: string;
|
||||
}>(),
|
||||
{
|
||||
enable: false,
|
||||
aggregate: false,
|
||||
}
|
||||
);
|
||||
|
||||
const select = ref(false);
|
||||
defineEmits(['on-select']);
|
||||
|
||||
const checked = ref(false);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -22,8 +24,9 @@ const select = ref(false);
|
||||
<div class="left-side" flex space-x-40px>
|
||||
<ab-checkbox
|
||||
small
|
||||
:model-value="select"
|
||||
@update:model-value="select = $event"
|
||||
:model-value="checked"
|
||||
@update:model-value="checked = $event"
|
||||
@click="() => $emit('on-select')"
|
||||
/>
|
||||
<div w-200px text-h3 truncate>{{ name }}</div>
|
||||
<div w-300px text-h3 truncate>{{ url }}</div>
|
||||
|
||||
@@ -14,8 +14,8 @@ const props = withDefaults(
|
||||
);
|
||||
|
||||
const emit = defineEmits(['update:value', 'click-search']);
|
||||
const { site, providers, bangumiInfo$} = storeToRefs(useSearchStore());
|
||||
const { getProviders, onInput } = useSearchStore();
|
||||
const {site, providers } = storeToRefs(useSearchStore());
|
||||
const {getProviders} = useSearchStore();
|
||||
|
||||
onMounted(() => {
|
||||
getProviders();
|
||||
@@ -60,8 +60,7 @@ function onSearch() {
|
||||
:value="value"
|
||||
:placeholder="placeholder"
|
||||
input-reset
|
||||
@keyup.enter="onInput"
|
||||
@input="onInput"
|
||||
@keyup.enter="onSearch"
|
||||
/>
|
||||
<div
|
||||
h-full
|
||||
@@ -104,15 +103,18 @@ function onSearch() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div abs top-84px left-200px z-98>
|
||||
<ab-bangumi-card
|
||||
name="name"
|
||||
season=1
|
||||
poster=""
|
||||
group="Lilith-Raws"
|
||||
type="search"
|
||||
/>
|
||||
</div>
|
||||
<!-- <div -->
|
||||
<!-- v-if="bangumiInfo$" -->
|
||||
<!-- abs top-84px left-200px z-98> -->
|
||||
<!-- <ab-bangumi-card-->
|
||||
<!-- v-for="i in bangumiInfo$" -->
|
||||
<!-- :key="i.id"-->
|
||||
<!-- :poster="i.poster_link ?? ''" -->
|
||||
<!-- :name="i.official_title" -->
|
||||
<!-- :season="i.season" -->
|
||||
<!-- :group="i.group_name" -->
|
||||
<!-- /> -->
|
||||
<!-- </div> -->
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -15,7 +15,6 @@ const props = withDefaults(
|
||||
items: Array<SelectItem | string>;
|
||||
}>(),
|
||||
{
|
||||
items: ['test1', 'test2'],
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ const items = [
|
||||
},
|
||||
];
|
||||
|
||||
const onSearchFocus = ref(false);
|
||||
|
||||
onBeforeMount(() => {
|
||||
onUpdate();
|
||||
});
|
||||
@@ -68,14 +70,14 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div h-60px bg-theme-row text-white rounded-12px fx-cer px-24px>
|
||||
<div h-60px bg-theme-row text-white rounded-16px fx-cer px-24px>
|
||||
<div flex space-x-16px>
|
||||
<div fx-cer space-x-16px>
|
||||
<img src="/images/logo-light.svg" alt="favicon" wh-24px/>
|
||||
<img src="/images/AutoBangumi.svg" alt="AutoBangumi" h-24px rel top-2px/>
|
||||
<img v-show="onSearchFocus === false" src="/images/AutoBangumi.svg" alt="AutoBangumi" h-24px rel top-2px/>
|
||||
</div>
|
||||
|
||||
<ab-search v-model:value="search"/>
|
||||
<ab-search/>
|
||||
</div>
|
||||
|
||||
<div ml-auto>
|
||||
|
||||
@@ -67,7 +67,8 @@
|
||||
"url": "Url",
|
||||
"status": "Status",
|
||||
"delete": "Delete",
|
||||
"disable": "Disable"
|
||||
"disable": "Disable",
|
||||
"enable": "Enable",
|
||||
},
|
||||
"player": {
|
||||
"hit": "Please set up the media player"
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
"status": "状态",
|
||||
"delete": "删除",
|
||||
"disable": "禁用",
|
||||
"enable": "启用",
|
||||
},
|
||||
"player": {
|
||||
"hit": "请设置媒体播放器地址"
|
||||
|
||||
@@ -22,6 +22,7 @@ definePage({
|
||||
:poster="i.poster_link ?? ''"
|
||||
:name="i.official_title"
|
||||
:season="i.season"
|
||||
type="primary"
|
||||
@click="() => openEditPopup(i)"
|
||||
></ab-bangumi-card>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
const {rss, selectedRSS} = storeToRefs(useRSSStore());
|
||||
const {getAll, deleteSelected, disableSelected, appendSelected} = useRSSStore();
|
||||
const {getAll, deleteSelected, disableSelected, enableSelected, handleCheckboxClicked} = useRSSStore();
|
||||
|
||||
onActivated(() => {
|
||||
getAll();
|
||||
@@ -36,16 +36,18 @@ definePage({
|
||||
:url="i.url"
|
||||
:enable="i.enabled"
|
||||
:parser="i.parser"
|
||||
:aggregate="i.aggregate">
|
||||
:aggregate="i.aggregate"
|
||||
@on-select="handleCheckboxClicked(i.id)"
|
||||
>
|
||||
</ab-rss-item>
|
||||
</div>
|
||||
<div line my-12px></div>
|
||||
<div text-h2>
|
||||
{{ selectedRSS }}
|
||||
</div>
|
||||
<div flex="~ justify-end" space-x-10px>
|
||||
<ab-button @click="disableSelected">{{ $t('rss.disable') }}</ab-button>
|
||||
<ab-button class="type-warn" @click="deleteSelected">{{ $t('rss.delete') }}</ab-button>
|
||||
<div v-if="selectedRSS.length > 0">
|
||||
<div line my-12px></div>
|
||||
<div flex="~ justify-end" space-x-10px>
|
||||
<ab-button @click="enableSelected">{{ $t('rss.enable') }}</ab-button>
|
||||
<ab-button @click="disableSelected">{{ $t('rss.disable') }}</ab-button>
|
||||
<ab-button class="type-warn" @click="deleteSelected">{{ $t('rss.delete') }}</ab-button>
|
||||
</div>
|
||||
</div>
|
||||
</ab-container>
|
||||
</div>
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
import type { RSS } from '#/rss';
|
||||
import type { ApiSuccess } from '#/api';
|
||||
import type {RSS} from '#/rss';
|
||||
import type {ApiSuccess} from '#/api';
|
||||
|
||||
export const useRSSStore = defineStore('rss', () => {
|
||||
const message = useMessage();
|
||||
const rss = ref<RSS[]>();
|
||||
const selectedRSS= ref<number[]>([]);
|
||||
const selectedRSS = ref<number[]>([]);
|
||||
|
||||
const { execute: getAll, onResult: onRSSResult } = useApi(
|
||||
const {execute: getAll, onResult: onRSSResult} = useApi(
|
||||
apiRSS.get
|
||||
);
|
||||
const { execute: updateRSS, onResult: onUpdateRSSResult } = useApi(
|
||||
const {execute: updateRSS, onResult: onUpdateRSSResult} = useApi(
|
||||
apiRSS.update
|
||||
);
|
||||
const { execute: disableRSS, onResult: onDisableRSSResult} = useApi(
|
||||
const {execute: disableRSS, onResult: onDisableRSSResult} = useApi(
|
||||
apiRSS.disableMany
|
||||
);
|
||||
const { execute: deleteRSS, onResult: onDeleteRSSResult } = useApi(
|
||||
const {execute: deleteRSS, onResult: onDeleteRSSResult} = useApi(
|
||||
apiRSS.deleteMany
|
||||
);
|
||||
|
||||
const {execute: enableRSS, onResult: onEnableRSSResult} = useApi(
|
||||
apiRSS.enableMany
|
||||
);
|
||||
|
||||
|
||||
onRSSResult((res) => {
|
||||
function sort(arr: RSS[]) {
|
||||
@@ -43,18 +47,28 @@ export const useRSSStore = defineStore('rss', () => {
|
||||
deleteRSS(selectedRSS.value);
|
||||
}
|
||||
|
||||
function enableSelected() {
|
||||
enableRSS(selectedRSS.value);
|
||||
}
|
||||
|
||||
function actionSuccess(apiRes: ApiSuccess) {
|
||||
message.success(apiRes.msg_en);
|
||||
refresh();
|
||||
}
|
||||
|
||||
function appendSelected(id: number) {
|
||||
selectedRSS.value.push(id);
|
||||
function handleCheckboxClicked(id: number) {
|
||||
if (selectedRSS.value.includes(id)) {
|
||||
// delete id in list
|
||||
selectedRSS.value = selectedRSS.value.filter((e) => e !== id);
|
||||
} else {
|
||||
selectedRSS.value.push(id)
|
||||
}
|
||||
}
|
||||
|
||||
onUpdateRSSResult(actionSuccess);
|
||||
onDeleteRSSResult(actionSuccess);
|
||||
onDisableRSSResult(actionSuccess)
|
||||
onDisableRSSResult(actionSuccess);
|
||||
onEnableRSSResult(actionSuccess);
|
||||
|
||||
return {
|
||||
rss,
|
||||
@@ -63,6 +77,7 @@ export const useRSSStore = defineStore('rss', () => {
|
||||
selectedRSS,
|
||||
disableSelected,
|
||||
deleteSelected,
|
||||
appendSelected,
|
||||
enableSelected,
|
||||
handleCheckboxClicked,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ import type {BangumiRule} from "#/bangumi";
|
||||
|
||||
export const useSearchStore = defineStore('search', () => {
|
||||
const input$ = new Subject<string>();
|
||||
const onInput = (e: Event) => input$.next(e.target);
|
||||
// const onInput = (e: Event) => input$.next(e.target);
|
||||
const providers = ref<string[]>(['mikan', 'dmhy', 'nyaa']);
|
||||
const site = ref<string>('mikan');
|
||||
|
||||
@@ -24,30 +24,29 @@ export const useSearchStore = defineStore('search', () => {
|
||||
providers.value = res;
|
||||
});
|
||||
|
||||
input$.pipe(
|
||||
debounceTime(1000),
|
||||
tap((input: string) => {
|
||||
console.log('input', input)
|
||||
// clear Search Result List
|
||||
|
||||
}),
|
||||
switchMap((input: string) => apiSearch.get(input, site.value)),
|
||||
tap((bangumi: BangumiRule) => console.log(bangumi)),
|
||||
tap((bangumi: BangumiRule) => {
|
||||
console.log('bangumi', bangumi)
|
||||
// set bangumi info to Search Result List
|
||||
}),
|
||||
).subscribe({
|
||||
complete() {
|
||||
// end of stream, stop loading animation
|
||||
}
|
||||
}, (err) => {
|
||||
console.error(err);
|
||||
});
|
||||
// input$.pipe(
|
||||
// debounceTime(1000),
|
||||
// tap((input: string) => {
|
||||
// console.log('input', input)
|
||||
// // clear Search Result List
|
||||
//
|
||||
// }),
|
||||
// switchMap((input: string) => apiSearch.get(input, site.value)),
|
||||
// tap((bangumi: BangumiRule) => console.log(bangumi)),
|
||||
// tap((bangumi: BangumiRule) => {
|
||||
// console.log('bangumi', bangumi)
|
||||
// // set bangumi info to Search Result List
|
||||
// }),
|
||||
// ).subscribe({
|
||||
// complete() {
|
||||
// // end of stream, stop loading animation
|
||||
// }
|
||||
// }, (err) => {
|
||||
// console.error(err);
|
||||
// });
|
||||
|
||||
|
||||
return {
|
||||
onInput,
|
||||
|
||||
bangumiInfo$,
|
||||
site,
|
||||
|
||||
1
webui/types/dts/components.d.ts
vendored
1
webui/types/dts/components.d.ts
vendored
@@ -18,7 +18,6 @@ declare module '@vue/runtime-core' {
|
||||
AbContainer: typeof import('./../../src/components/ab-container.vue')['default']
|
||||
AbEditRule: typeof import('./../../src/components/ab-edit-rule.vue')['default']
|
||||
AbFoldPanel: typeof import('./../../src/components/ab-fold-panel.vue')['default']
|
||||
AbInfoCard: typeof import('./../../src/components/ab-info-card.vue')['default']
|
||||
AbLabel: typeof import('./../../src/components/ab-label.vue')['default']
|
||||
AbPageTitle: typeof import('./../../src/components/basic/ab-page-title.vue')['default']
|
||||
AbPopup: typeof import('./../../src/components/ab-popup.vue')['default']
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
defineConfig,
|
||||
presetAttributify,
|
||||
presetIcons,
|
||||
presetUno,
|
||||
defineConfig,
|
||||
presetAttributify,
|
||||
presetIcons,
|
||||
presetUno,
|
||||
} from 'unocss';
|
||||
import presetRemToPx from '@unocss/preset-rem-to-px';
|
||||
|
||||
@@ -51,33 +51,33 @@ export default defineConfig({
|
||||
shortcuts: [
|
||||
[/^wh-(.*)$/, ([, t]) => `w-${t} h-${t}`],
|
||||
|
||||
[
|
||||
'layout-container',
|
||||
'wh-screen min-w-1024px min-h-768px p-16px space-y-12px flex flex-col bg-page',
|
||||
],
|
||||
[
|
||||
'layout-main',
|
||||
'flex space-x-20px overflow-hidden h-[calc(100vh_-_2_*_16px_-_60px_-_12px)]',
|
||||
],
|
||||
['layout-content', 'overflow-hidden h-full flex flex-col flex-1'],
|
||||
[
|
||||
'layout-container',
|
||||
'wh-screen min-w-1024px min-h-768px p-16px space-y-12px flex flex-col bg-page',
|
||||
],
|
||||
[
|
||||
'layout-main',
|
||||
'flex space-x-20px overflow-hidden h-[calc(100vh_-_2_*_16px_-_60px_-_12px)]',
|
||||
],
|
||||
['layout-content', 'overflow-hidden h-full flex flex-col flex-1'],
|
||||
|
||||
['rel', 'relative'],
|
||||
['abs', 'absolute'],
|
||||
['fx-cer', 'flex items-center'],
|
||||
['f-cer', 'fx-cer justify-center'],
|
||||
['text-h1', 'text-24px'],
|
||||
['text-h2', 'text-20px'],
|
||||
['text-h3', 'text-16px'],
|
||||
['text-main', 'text-12px'],
|
||||
[
|
||||
'ab-input',
|
||||
'outline-none min-w-0 w-200px h-28px px-12px text-main text-right rounded-6px border-1 border-black shadow-inset hover:border-color-[#7A46AE]',
|
||||
['rel', 'relative'],
|
||||
['abs', 'absolute'],
|
||||
['fx-cer', 'flex items-center'],
|
||||
['f-cer', 'fx-cer justify-center'],
|
||||
['text-h1', 'text-24px'],
|
||||
['text-h2', 'text-20px'],
|
||||
['text-h3', 'text-16px'],
|
||||
['text-main', 'text-12px'],
|
||||
[
|
||||
'ab-input',
|
||||
'outline-none min-w-0 w-200px h-28px px-12px text-main text-right rounded-6px border-1 border-black shadow-inset hover:border-color-[#7A46AE]',
|
||||
],
|
||||
['input-error', 'border-color-[#CA0E0E]'],
|
||||
['is-btn', 'cursor-pointer select-none'],
|
||||
['is-disabled', 'cursor-not-allowed select-none'],
|
||||
['input-reset', 'bg-transparent min-w-0 flex-1 outline-none'],
|
||||
['btn-click', 'hover:scale-110 active:scale-100'],
|
||||
['line', 'w-full h-1px bg-[#DFE1EF]'],
|
||||
],
|
||||
['input-error', 'border-color-[#CA0E0E]'],
|
||||
['is-btn', 'cursor-pointer select-none'],
|
||||
['is-disabled', 'cursor-not-allowed select-none'],
|
||||
['input-reset', 'bg-transparent min-w-0 flex-1 outline-none'],
|
||||
['btn-click', 'hover:scale-110 active:scale-100'],
|
||||
['line', 'w-full h-1px bg-[#DFE1EF]'],
|
||||
],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user