mirror of
https://github.com/EstrellaXD/Auto_Bangumi.git
synced 2026-04-13 17:19:56 +08:00
webui: update search card.
backend: update api url and rss manage api route.
This commit is contained in:
@@ -64,6 +64,23 @@ async def disable_rss(rss_id: int, current_user=Depends(get_current_user)):
|
||||
)
|
||||
|
||||
|
||||
@router.post(path="/disable/many", response_model=APIResponse)
|
||||
async def disable_many_rss(rss_ids: list[int], current_user=Depends(get_current_user)):
|
||||
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 失败。"},
|
||||
)
|
||||
|
||||
|
||||
@router.patch(path="/update/{rss_id}", response_model=APIResponse)
|
||||
async def update_rss(
|
||||
rss_id: int, data: RSSUpdate, current_user=Depends(get_current_user)
|
||||
|
||||
@@ -9,7 +9,7 @@ from module.models import Bangumi
|
||||
router = APIRouter(prefix="/search", tags=["search"])
|
||||
|
||||
|
||||
@router.get("/", response_model=list[Bangumi])
|
||||
@router.get("/bangumi", response_model=list[Bangumi])
|
||||
async def search_torrents(
|
||||
site: str = "mikan",
|
||||
keywords: str = Query(None),
|
||||
|
||||
@@ -59,6 +59,10 @@ class RSSEngine(Database):
|
||||
msg_zh="RSS 添加失败。",
|
||||
)
|
||||
|
||||
def disable_list(self, rss_id_list: list[int]):
|
||||
for rss_id in rss_id_list:
|
||||
self.rss.disable(rss_id)
|
||||
|
||||
def pull_rss(self, rss_item: RSSItem) -> list[Torrent]:
|
||||
torrents = self._get_torrents(rss_item)
|
||||
new_torrents = self.torrent.check_new(torrents)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { BangumiAPI, BangumiRule, BangumiUpdate } from '#/bangumi';
|
||||
import type { BangumiAPI, BangumiRule } from '#/bangumi';
|
||||
import type { ApiSuccess } from '#/api';
|
||||
|
||||
|
||||
|
||||
@@ -18,11 +18,21 @@ export const apiRSS = {
|
||||
return data!;
|
||||
},
|
||||
|
||||
async deleteMany(rss_list: number[]) {
|
||||
const { data } = await axios.post<ApiSuccess>(`api/v1/rss/delete/many`, rss_list);
|
||||
return data!;
|
||||
},
|
||||
|
||||
async disable(rss_id: number) {
|
||||
const { data } = await axios.patch<ApiSuccess>(`api/v1/rss/disable/${rss_id}`);
|
||||
return data!;
|
||||
},
|
||||
|
||||
async disableMany(rss_list: number[]) {
|
||||
const { data } = await axios.post<ApiSuccess>(`api/v1/rss/disable/many`, rss_list);
|
||||
return data!;
|
||||
},
|
||||
|
||||
async update(rss_id: number, rss: RSS) {
|
||||
const { data } = await axios.patch<ApiSuccess>(`api/v1/rss/update/${rss_id}`, rss);
|
||||
return data!;
|
||||
|
||||
@@ -55,7 +55,7 @@ export const apiSearch = {
|
||||
get(keyword: string, site = 'mikan'): Observable<BangumiRule> {
|
||||
const bangumiInfo$ = new Observable<BangumiRule>(observer => {
|
||||
const eventSource = new EventSource(
|
||||
`api/v1/search?site=${site}&keyword=${encodeURIComponent(keyword)}`,
|
||||
`api/v1/search/bangumi?site=${site}&keywords=${encodeURIComponent(keyword)}`,
|
||||
{ withCredentials: true },
|
||||
);
|
||||
|
||||
|
||||
@@ -1,74 +1,32 @@
|
||||
<script lang="ts" setup>
|
||||
import {Search} from '@icon-park/vue-next';
|
||||
import {Down, Search} from '@icon-park/vue-next';
|
||||
import {ref} from 'vue';
|
||||
import {Down, Up} from '@icon-park/vue-next';
|
||||
import {isString} from "lodash";
|
||||
import type {SelectItem} from "#/components";
|
||||
import {apiSearch} from "@/api/search";
|
||||
import {
|
||||
Subject,
|
||||
tap,
|
||||
map,
|
||||
switchMap,
|
||||
debounceTime,
|
||||
} from "rxjs";
|
||||
import type {BangumiRule} from "#/bangumi";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
value?: string;
|
||||
provider: string[];
|
||||
placeholder?: string;
|
||||
}>(),
|
||||
{
|
||||
value: '',
|
||||
provider: ['Mikan', 'Dmhy', 'Nyaa'],
|
||||
placeholder: '',
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits(['update:value', 'click-search']);
|
||||
const { site, providers, bangumiInfo$} = storeToRefs(useSearchStore());
|
||||
const { getProviders, onInput } = useSearchStore();
|
||||
|
||||
const selected = ref<SelectItem | string>(
|
||||
(props.provider?.[0] ?? '')
|
||||
);
|
||||
onMounted(() => {
|
||||
getProviders();
|
||||
});
|
||||
|
||||
const selectedProvider = computed(() => {
|
||||
if (isString(selected.value)) {
|
||||
return selected.value;
|
||||
} else {
|
||||
return selected.value.label ?? selected.value.value;
|
||||
}
|
||||
return site.value || '';
|
||||
});
|
||||
|
||||
const onSelect = ref(false);
|
||||
|
||||
const input$ = new Subject<string>();
|
||||
const onInput = (value: string) => {
|
||||
input$.next(value);
|
||||
};
|
||||
|
||||
const bangumiInfo$ = apiSearch.get('魔女之旅')
|
||||
|
||||
input$.pipe(
|
||||
debounceTime(500),
|
||||
tap((input: string) => {
|
||||
console.log(input);
|
||||
}),
|
||||
switchMap((input: string) => {
|
||||
return apiSearch.get(input, site);
|
||||
}),
|
||||
tap((bangumi: BangumiRule) => console.log(bangumi)),
|
||||
tap((bangumi: BangumiRule) => {
|
||||
console.log('bangumi', bangumi)
|
||||
// set bangumi info to Search Result List
|
||||
}),
|
||||
).subscribe({
|
||||
complete() {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
function onSearch() {
|
||||
emit('click-search', props.value);
|
||||
}
|
||||
@@ -102,7 +60,8 @@ function onSearch() {
|
||||
:value="value"
|
||||
:placeholder="placeholder"
|
||||
input-reset
|
||||
@keyup.enter="onSearch"
|
||||
@keyup.enter="onInput"
|
||||
@input="onInput"
|
||||
/>
|
||||
<div
|
||||
h-full
|
||||
@@ -111,10 +70,10 @@ function onSearch() {
|
||||
px-12px
|
||||
w-100px
|
||||
is-btn
|
||||
@click="() => onSelect = !onSelect"
|
||||
class="provider-select"
|
||||
@click="() => onSelect = !onSelect"
|
||||
>
|
||||
<div text-h3>
|
||||
<div text-h3 truncate>
|
||||
{{ selectedProvider }}
|
||||
</div>
|
||||
<div class="provider-select">
|
||||
@@ -124,12 +83,12 @@ function onSearch() {
|
||||
</div>
|
||||
<div v-show="onSelect" abs top-84px left-540px w-100px rounded-12px shadow bg-white z-99 overflow-hidden>
|
||||
<div
|
||||
v-for="i in provider"
|
||||
v-for="i in providers"
|
||||
:key="i"
|
||||
hover:bg-theme-row
|
||||
is-btn
|
||||
@click="() => {
|
||||
selected = i;
|
||||
site = i;
|
||||
onSelect = false;
|
||||
}"
|
||||
|
||||
@@ -139,11 +98,21 @@ function onSearch() {
|
||||
text-primary
|
||||
hover:text-white
|
||||
p-12px
|
||||
truncate
|
||||
>
|
||||
{{ i }}
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
const {rss} = storeToRefs(useRSSStore());
|
||||
const {getAll, updateRSS, deleteRSS} = useRSSStore();
|
||||
const {rss, selectedRSS} = storeToRefs(useRSSStore());
|
||||
const {getAll, deleteSelected, disableSelected, appendSelected} = useRSSStore();
|
||||
|
||||
onActivated(() => {
|
||||
getAll();
|
||||
@@ -39,11 +39,14 @@ definePage({
|
||||
:aggregate="i.aggregate">
|
||||
</ab-rss-item>
|
||||
</div>
|
||||
<!-- <div line my-12px></div> -->
|
||||
<!-- <div flex="~ justify-end" space-x-10px> -->
|
||||
<!-- <ab-button @click="updateRSS">{{ $t('rss.disable') }}</ab-button> -->
|
||||
<!-- <ab-button class="type-warn" @click="deleteRSS">{{ $t('rss.delete') }}</ab-button> -->
|
||||
<!-- </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>
|
||||
</ab-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { ApiSuccess } from '#/api';
|
||||
export const useRSSStore = defineStore('rss', () => {
|
||||
const message = useMessage();
|
||||
const rss = ref<RSS[]>();
|
||||
const selectedRSS= ref<number[]>([]);
|
||||
|
||||
const { execute: getAll, onResult: onRSSResult } = useApi(
|
||||
apiRSS.get
|
||||
@@ -12,10 +13,10 @@ export const useRSSStore = defineStore('rss', () => {
|
||||
apiRSS.update
|
||||
);
|
||||
const { execute: disableRSS, onResult: onDisableRSSResult} = useApi(
|
||||
apiRSS.disable
|
||||
apiRSS.disableMany
|
||||
);
|
||||
const { execute: deleteRSS, onResult: onDeleteRSSResult } = useApi(
|
||||
apiRSS.delete
|
||||
apiRSS.deleteMany
|
||||
);
|
||||
|
||||
|
||||
@@ -34,11 +35,23 @@ export const useRSSStore = defineStore('rss', () => {
|
||||
getAll();
|
||||
}
|
||||
|
||||
function disableSelected() {
|
||||
disableRSS(selectedRSS.value);
|
||||
}
|
||||
|
||||
function deleteSelected() {
|
||||
deleteRSS(selectedRSS.value);
|
||||
}
|
||||
|
||||
function actionSuccess(apiRes: ApiSuccess) {
|
||||
message.success(apiRes.msg_en);
|
||||
refresh();
|
||||
}
|
||||
|
||||
function appendSelected(id: number) {
|
||||
selectedRSS.value.push(id);
|
||||
}
|
||||
|
||||
onUpdateRSSResult(actionSuccess);
|
||||
onDeleteRSSResult(actionSuccess);
|
||||
onDisableRSSResult(actionSuccess)
|
||||
@@ -47,8 +60,9 @@ export const useRSSStore = defineStore('rss', () => {
|
||||
rss,
|
||||
getAll,
|
||||
refresh,
|
||||
updateRSS,
|
||||
disableRSS,
|
||||
deleteRSS,
|
||||
selectedRSS,
|
||||
disableSelected,
|
||||
deleteSelected,
|
||||
appendSelected,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,2 +1,57 @@
|
||||
import {
|
||||
Subject,
|
||||
tap,
|
||||
map,
|
||||
switchMap,
|
||||
debounceTime,
|
||||
} from "rxjs";
|
||||
import type {BangumiRule} from "#/bangumi";
|
||||
import type {ApiSuccess} from "#/api";
|
||||
|
||||
|
||||
export const useSearchStore = defineStore('search', () => {
|
||||
const input$ = new Subject<string>();
|
||||
const onInput = (e: Event) => input$.next(e.target);
|
||||
const providers = ref<string[]>(['mikan', 'dmhy', 'nyaa']);
|
||||
const site = ref<string>('mikan');
|
||||
|
||||
const bangumiInfo$ = apiSearch.get('魔女之旅');
|
||||
|
||||
const {execute: getProviders, onResult: onGetProvidersResult} = useApi(
|
||||
apiSearch.getProvider
|
||||
);
|
||||
|
||||
onGetProvidersResult((res) => {
|
||||
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);
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
onInput,
|
||||
|
||||
bangumiInfo$,
|
||||
site,
|
||||
getProviders,
|
||||
providers,
|
||||
};
|
||||
});
|
||||
1
webui/types/dts/auto-imports.d.ts
vendored
1
webui/types/dts/auto-imports.d.ts
vendored
@@ -245,6 +245,7 @@ declare global {
|
||||
const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
|
||||
const useScroll: typeof import('@vueuse/core')['useScroll']
|
||||
const useScrollLock: typeof import('@vueuse/core')['useScrollLock']
|
||||
const useSearchStore: typeof import('../../src/store/search')['useSearchStore']
|
||||
const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
|
||||
const useShare: typeof import('@vueuse/core')['useShare']
|
||||
const useSlots: typeof import('vue')['useSlots']
|
||||
|
||||
Reference in New Issue
Block a user