Add background task notification

This commit is contained in:
LASER-Yi 2021-05-09 21:23:58 +08:00
parent 8572331ebd
commit 678b2f0e55
16 changed files with 157 additions and 100 deletions

View File

@ -25,7 +25,3 @@ def show_message(msg):
def show_progress(id, name, value, count):
event_stream(type="progress", payload={"id": id, "name": name, "value": value, "count": count})
def hide_progress(id, delay=3):
event_stream(type="progress", action="delete", payload={"id": id, "delay": delay})

View File

@ -9,7 +9,7 @@ from config import settings, url_sonarr
from helper import path_mappings
from list_subtitles import store_subtitles, series_full_scan_subtitles
from get_subtitle import episode_download_subtitles
from event_handler import event_stream, show_progress, hide_progress
from event_handler import event_stream, show_progress
headers = {"User-Agent": os.environ["SZ_USER_AGENT"]}
@ -63,8 +63,6 @@ def sync_episodes():
else:
episodes_to_add.append(episodeParser(episode))
hide_progress(id='episodes_progress')
# Remove old episodes from DB
removed_episodes = list(set(current_episodes_db_list) - set(current_episodes_sonarr))

View File

@ -12,7 +12,7 @@ from get_rootfolder import check_radarr_rootfolder
from get_subtitle import movies_download_subtitles
from database import database, dict_converter, get_exclusion_clause
from event_handler import event_stream, show_progress, hide_progress
from event_handler import event_stream, show_progress
headers = {"User-Agent": os.environ["SZ_USER_AGENT"]}
@ -87,8 +87,6 @@ def update_movies():
movie_default_profile=movie_default_profile,
audio_profiles=audio_profiles))
hide_progress(id='movies_progress')
# Remove old movies from DB
removed_movies = list(set(current_movies_db_list) - set(current_movies_radarr))

View File

@ -10,7 +10,7 @@ from get_rootfolder import check_sonarr_rootfolder
from database import database, dict_converter
from utils import get_sonarr_version
from helper import path_mappings
from event_handler import event_stream, show_progress, hide_progress
from event_handler import event_stream, show_progress
headers = {"User-Agent": os.environ["SZ_USER_AGENT"]}
@ -66,8 +66,6 @@ def update_series():
tags_dict=tagsDict, serie_default_profile=serie_default_profile,
audio_profiles=audio_profiles))
hide_progress(id='series_progress')
# Remove old series from DB
removed_series = list(set(current_shows_db_list) - set(current_shows_sonarr))

View File

@ -33,7 +33,7 @@ from subsyncer import subsync
from guessit import guessit
from database import database, dict_mapper, get_exclusion_clause, get_profiles_list, get_audio_profile_languages, \
get_desired_languages
from event_handler import event_stream, show_progress, hide_progress
from event_handler import event_stream, show_progress,
from embedded_subs_reader import parse_video_metadata
from analytics import track_event
@ -806,7 +806,7 @@ def series_download_subtitles(no):
logging.info("BAZARR All providers are throttled")
break
hide_progress(id='series_search_progress_{}'.format(no))
def episode_download_subtitles(no):
@ -929,8 +929,6 @@ def movies_download_subtitles(no):
logging.info("BAZARR All providers are throttled")
break
hide_progress(id='movie_search_progress_{}'.format(no))
def wanted_download_subtitles(path):
episodes_details = database.execute("SELECT table_episodes.path, table_episodes.missing_subtitles, "
@ -1100,7 +1098,7 @@ def wanted_search_missing_subtitles_series():
logging.info("BAZARR All providers are throttled")
return
hide_progress(id='wanted_episodes_progress')
logging.info('BAZARR Finished searching for missing Series Subtitles. Check History for more information.')
@ -1124,7 +1122,7 @@ def wanted_search_missing_subtitles_movies():
logging.info("BAZARR All providers are throttled")
return
hide_progress(id='wanted_movies_progress')
logging.info('BAZARR Finished searching for missing Movies Subtitles. Check History for more information.')
@ -1402,7 +1400,7 @@ def upgrade_subtitles():
language_code, provider, score, subs_id, subs_path)
send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message)
hide_progress('upgrade_episodes_progress')
if settings.general.getboolean('use_radarr'):
for i, movie in enumerate(movies_to_upgrade, 1):
@ -1468,7 +1466,7 @@ def upgrade_subtitles():
history_log_movie(3, movie['radarrId'], message, path, language_code, provider, score, subs_id, subs_path)
send_notifications_movie(movie['radarrId'], message)
hide_progress(id='upgrade_movies_progress')
logging.info('BAZARR Finished searching for Subtitles to upgrade. Check History for more information.')

View File

@ -15,7 +15,7 @@ from config import settings
from helper import path_mappings, get_subtitle_destination_folder
from embedded_subs_reader import embedded_subs_reader
from event_handler import event_stream, show_progress, hide_progress
from event_handler import event_stream, show_progress
from charamel import Detector
gc.enable()
@ -488,8 +488,6 @@ def series_full_scan_subtitles():
value=i,
count=count_episodes)
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']))
hide_progress(id='episodes_disk_scan')
gc.collect()
@ -505,8 +503,6 @@ def movies_full_scan_subtitles():
count=count_movies)
store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']))
hide_progress(id='movies_disk_scan')
gc.collect()

View File

@ -1,4 +1,3 @@
import { createAction } from "redux-actions";
import {
ActionCallback,
ActionDispatcher,
@ -122,8 +121,3 @@ export function createCallbackAction<T extends AsyncActionCreator>(
return (...args: Parameters<T>) =>
callbackActionFactory(fn(args), success, error);
}
// Helper
export function createDeleteAction(type: string): SocketIO.ActionFn {
return createAction(type, (id?: number[]) => id ?? []);
}

View File

@ -1,3 +1,4 @@
import { createDeleteAction } from "../../@socketio/reducer";
import { MoviesApi } from "../../apis";
import {
MOVIES_DELETE_ITEMS,
@ -7,7 +8,7 @@ import {
MOVIES_UPDATE_LIST,
MOVIES_UPDATE_WANTED_LIST,
} from "../constants";
import { createAsyncAction, createDeleteAction } from "./factory";
import { createAsyncAction } from "./factory";
export const movieUpdateList = createAsyncAction(
MOVIES_UPDATE_LIST,

View File

@ -1,3 +1,4 @@
import { createDeleteAction } from "../../@socketio/reducer";
import { EpisodesApi, SeriesApi } from "../../apis";
import {
SERIES_DELETE_EPISODES,
@ -9,7 +10,7 @@ import {
SERIES_UPDATE_LIST,
SERIES_UPDATE_WANTED_LIST,
} from "../constants";
import { createAsyncAction, createDeleteAction } from "./factory";
import { createAsyncAction } from "./factory";
export const seriesUpdateWantedList = createAsyncAction(
SERIES_UPDATE_WANTED_LIST,

View File

@ -54,7 +54,10 @@ export function useSystemLogs() {
export function useSystemTasks() {
const items = useReduxStore((s) => s.system.tasks);
const update = useReduxAction(systemUpdateTasks);
useSocketIOReducer("task", update);
const reducer = useMemo<SocketIO.Reducer>(() => ({ key: "task", update }), [
update,
]);
useSocketIOReducer(reducer);
useEffect(() => {
update();
@ -245,10 +248,18 @@ export function useEpisodesBy(seriesId?: number) {
const actionById = useReduxAction(episodeUpdateById);
const wrapActionById = useWrapToOptionalId(actionById);
const deleteAction = useReduxAction(episodeDeleteItems);
useSocketIOReducer("episode", undefined, wrapActionById, deleteAction);
const episodeReducer = useMemo<SocketIO.Reducer>(
() => ({ key: "episode", update: wrapActionById, delete: deleteAction }),
[wrapActionById, deleteAction]
);
useSocketIOReducer(episodeReducer);
const wrapAction = useWrapToOptionalId(action);
useSocketIOReducer("series", undefined, wrapAction);
const seriesReducer = useMemo<SocketIO.Reducer>(
() => ({ key: "series", update: wrapAction }),
[wrapAction]
);
useSocketIOReducer(seriesReducer);
useEffect(() => {
update();
@ -315,7 +326,15 @@ export function useWantedSeries() {
const updateAction = useWrapToOptionalId(update);
const deleteAction = useReduxAction(seriesDeleteWantedItems);
useSocketIOReducer("episode-wanted", undefined, updateAction, deleteAction);
const reducer = useMemo<SocketIO.Reducer>(
() => ({
key: "episode-wanted",
update: updateAction,
delete: deleteAction,
}),
[updateAction, deleteAction]
);
useSocketIOReducer(reducer);
return stateBuilder(items, update);
}
@ -326,7 +345,15 @@ export function useWantedMovies() {
const updateAction = useWrapToOptionalId(update);
const deleteAction = useReduxAction(movieDeleteWantedItems);
useSocketIOReducer("movie-wanted", undefined, updateAction, deleteAction);
const reducer = useMemo<SocketIO.Reducer>(
() => ({
key: "movie-wanted",
update: updateAction,
delete: deleteAction,
}),
[updateAction, deleteAction]
);
useSocketIOReducer(reducer);
return stateBuilder(items, update);
}
@ -334,8 +361,11 @@ export function useWantedMovies() {
export function useBlacklistMovies() {
const update = useReduxAction(movieUpdateBlacklist);
const items = useReduxStore((d) => d.movie.blacklist);
useSocketIOReducer("movie-blacklist", update);
const reducer = useMemo<SocketIO.Reducer>(
() => ({ key: "movie-blacklist", update }),
[update]
);
useSocketIOReducer(reducer);
useEffect(() => {
update();
@ -346,8 +376,11 @@ export function useBlacklistMovies() {
export function useBlacklistSeries() {
const update = useReduxAction(seriesUpdateBlacklist);
const items = useReduxStore((d) => d.series.blacklist);
useSocketIOReducer("episode-blacklist", update);
const reducer = useMemo<SocketIO.Reducer>(
() => ({ key: "episode-blacklist", update }),
[update]
);
useSocketIOReducer(reducer);
useEffect(() => {
update();
@ -358,8 +391,11 @@ export function useBlacklistSeries() {
export function useMoviesHistory() {
const update = useReduxAction(movieUpdateHistoryList);
const items = useReduxStore((s) => s.movie.historyList);
useSocketIOReducer("movie-history", update);
const reducer = useMemo<SocketIO.Reducer>(
() => ({ key: "movie-history", update }),
[update]
);
useSocketIOReducer(reducer);
useEffect(() => {
update();
@ -370,8 +406,11 @@ export function useMoviesHistory() {
export function useSeriesHistory() {
const update = useReduxAction(seriesUpdateHistoryList);
const items = useReduxStore((s) => s.series.historyList);
useSocketIOReducer("episode-history", update);
const reducer = useMemo<SocketIO.Reducer>(
() => ({ key: "episode-history", update }),
[update]
);
useSocketIOReducer(reducer);
useEffect(() => {
update();

View File

@ -39,7 +39,7 @@ const reducer = handleActions<ReduxStore.Site, any>(
action: Action<ReduxStore.Notification[]>
) => {
const notifications = uniqBy(
[...action.payload, ...state.notifications],
[...action.payload.reverse(), ...state.notifications],
(n) => n.id
);
return { ...state, notifications };
@ -51,7 +51,7 @@ const reducer = handleActions<ReduxStore.Site, any>(
},
[SITE_PROGRESS_ADD]: (state, action: Action<ReduxStore.Progress[]>) => {
const progress = uniqBy(
[...action.payload, ...state.progress],
[...action.payload.reverse(), ...state.progress],
(n) => n.id
);
return { ...state, progress };

View File

@ -1,29 +1,20 @@
import { useCallback, useEffect, useMemo } from "react";
import { useCallback, useEffect } from "react";
import Socketio from ".";
import { log } from "../utilites/logger";
export function useSocketIOReducer(
key: SocketIO.EventType,
any?: () => void,
update?: SocketIO.ActionFn,
remove?: SocketIO.ActionFn
) {
const reducer = useMemo<SocketIO.Reducer>(
() => ({ key, any, update, delete: remove }),
[key, any, update, remove]
);
export function useSocketIOReducer(reducer: SocketIO.Reducer) {
useEffect(() => {
Socketio.addReducer(reducer);
log("info", "listening to SocketIO event", key);
log("info", "listening to SocketIO event", reducer.key);
return () => {
Socketio.removeReducer(reducer);
};
}, [reducer, key]);
}, [reducer]);
}
export function useWrapToOptionalId(
fn: (id: number[]) => void
): SocketIO.ActionFn {
): SocketIO.ActionFn<number> {
return useCallback(
(id?: number[]) => {
if (id) {

View File

@ -27,7 +27,7 @@ class SocketIOClient {
this.socket.on("data", this.onEvent.bind(this));
this.events = [];
this.debounceReduce = debounce(this.reduce, 200);
this.debounceReduce = debounce(this.reduce, 20);
this.reducers = [];
}
@ -91,7 +91,7 @@ class SocketIOClient {
forIn(element, (ids, key) => {
ids = uniq(ids);
const action = handler[key as SocketIO.ActionType];
const action = handler[key as SocketIO.Action];
if (action) {
action(ids);
} else if (anyAction === undefined) {

View File

@ -1,3 +1,4 @@
import { createAction } from "redux-actions";
import {
badgeUpdateAll,
bootstrap,
@ -6,6 +7,7 @@ import {
seriesDeleteItems,
seriesUpdateList,
siteAddNotifications,
siteAddProgress,
siteInitializationFailed,
siteUpdateOffline,
systemUpdateLanguagesAll,
@ -13,10 +15,16 @@ import {
} from "../@redux/actions";
import reduxStore from "../@redux/store";
function bindToReduxStore(fn: (ids?: number[]) => any): SocketIO.ActionFn {
function bindToReduxStore(
fn: (ids?: number[]) => any
): SocketIO.ActionFn<number> {
return (ids?: number[]) => reduxStore.dispatch(fn(ids));
}
export function createDeleteAction(type: string): SocketIO.ActionFn<number> {
return createAction(type, (id?: number[]) => id ?? []);
}
export function createDefaultReducer(): SocketIO.Reducer[] {
return [
{
@ -44,7 +52,7 @@ export function createDefaultReducer(): SocketIO.Reducer[] {
},
{
key: "message",
update: (msg?: string[]) => {
update: (msg) => {
if (msg) {
const notifications = msg.map<ReduxStore.Notification>((message) => ({
message,
@ -57,6 +65,14 @@ export function createDefaultReducer(): SocketIO.Reducer[] {
}
},
},
{
key: "progress",
update: (progress) => {
if (progress) {
reduxStore.dispatch(siteAddProgress(progress));
}
},
},
{
key: "series",
update: bindToReduxStore(seriesUpdateList),

View File

@ -1,43 +1,55 @@
namespace SocketIO {
type EventType =
| "connect"
| "connect_error"
| "disconnect"
type Action = "update" | "delete";
type EventType = NumEventType | NullEventType | SpecialEventType;
type NumEventType =
| "movie"
| "series"
| "episode"
| "episode-history"
| "episode-blacklist"
| "episode-wanted"
| "movie-history"
| "movie-wanted";
type NullEventType =
| "connect"
| "connect_error"
| "disconnect"
| "episode-blacklist"
| "episode-history"
| "movie-blacklist"
| "movie-wanted"
| "movie-history"
| "badges"
| "task"
| "settings"
| "languages"
| "message";
| "languages";
type ActionType = "update" | "delete";
type SpecialEventType = "message" | "progress";
interface Event {
type: EventType;
action: ActionType;
payload: any; // TODO: Use specific types
}
type ActionFn = (payload?: any[]) => void;
type Reducer = {
key: EventType;
any?: () => any;
} & Partial<Record<ActionType, ActionFn>>;
type ActionRecord = OptionalRecord<
EventType,
OptionalRecord<ActionType, any[]>
type ReducerCreator<E extends EventType, T> = ValueOf<
{
[P in E]: {
key: P;
any?: () => void;
} & Partial<Record<Action, ActionFn<T>>>;
}
>;
type Event = {
type: EventType;
action: Action;
payload: any;
};
type ActionFn<T> = (payload?: T[]) => void;
type Reducer =
| ReducerCreator<NumEventType, number>
| ReducerCreator<NullEventType, null>
| ReducerCreator<"message", string>
| ReducerCreator<"progress", CustomEvent.Progress>;
type ActionRecord = OptionalRecord<EventType, OptionalRecord<Action, any[]>>;
namespace CustomEvent {
type Progress = ReduxStore.Progress;
}

View File

@ -4,9 +4,13 @@ import {
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { capitalize } from "lodash";
import React, { FunctionComponent, useCallback, useMemo } from "react";
import React, {
FunctionComponent,
useCallback,
useEffect,
useMemo,
} from "react";
import { ProgressBar, Toast } from "react-bootstrap";
import { useTimeoutWhen } from "rooks";
import {
siteRemoveNotifications,
siteRemoveProgress,
@ -48,7 +52,12 @@ const NotificationToast: FunctionComponent<MessageHolderProps> = (props) => {
id,
]);
useTimeoutWhen(remove, timeout);
useEffect(() => {
const handle = setTimeout(remove, timeout);
return () => {
clearTimeout(handle);
};
}, [props, remove, timeout]);
return (
<Toast onClose={remove} animation={false}>
@ -75,12 +84,22 @@ const ProgressToast: FunctionComponent<ProgressHolderProps> = ({
const removeProgress = useReduxAction(siteRemoveProgress);
const remove = useCallback(() => removeProgress(id), [removeProgress, id]);
// TODO: Auto remove
useEffect(() => {
const handle = setTimeout(remove, 5 * 1000);
return () => {
clearTimeout(handle);
};
}, [value, remove]);
const incomplete = value / count < 1;
return (
<Toast onClose={remove}>
<Toast.Header hidden={incomplete}>
<span className="mr-auto">Background Task</span>
</Toast.Header>
<Toast.Body>
<div className="mb-2 mt-1">
<div className="mb-2 mt-1 text-nowrap text-truncate">
<FontAwesomeIcon
className="mr-2"
icon={faPaperPlane}
@ -89,7 +108,7 @@ const ProgressToast: FunctionComponent<ProgressHolderProps> = ({
</div>
<ProgressBar
className="my-1"
animated
animated={incomplete}
now={value / count}
max={1}
label={`${value}/${count}`}