Skip to content

Commit

Permalink
Merge pull request #751 from YvonTre/fix(subtitle)-no-refresh-subtitles
Browse files Browse the repository at this point in the history
fix(subtitle): fix online subtitle problems
  • Loading branch information
ipy authored Jul 16, 2019
2 parents 818e303 + 23d2c5a commit 7597c81
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 102 deletions.
4 changes: 0 additions & 4 deletions src/renderer/components/PlayingView/SubtitleControl.vue
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,6 @@ export default {
enabledSecondarySub(val: boolean) {
if (!val) this.updateSubtitleType(true);
},
computedAvailableItems(val: SubtitleControlListItem[]) {
this.updateNoSubtitle(!val.length);
},
list(val: SubtitleControlListItem[]) {
this.computedAvailableItems = val.map((sub: SubtitleControlListItem) => ({
...sub,
Expand Down Expand Up @@ -348,7 +345,6 @@ export default {
changeSecondarySubtitle: smActions.changeSecondarySubtitle,
refreshSubtitles: smActions.refreshSubtitles,
deleteCurrentSubtitle: smActions.deleteSubtitlesByUuid,
updateNoSubtitle: subtitleActions.UPDATE_NO_SUBTITLE,
updateSubtitleType: subtitleActions.UPDATE_SUBTITLE_TYPE,
}),
offCurrentSubtitle() {
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/services/subtitle/searchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export async function fetchOnlineList(
return Sagi.mediaTranslate({
mediaIdentity, languageCode, hints: hints || basename(videoSrc, extname(videoSrc)),
format: '', startTime: 0, // tempoary useless params according to server-side
});
}).catch(() => []);
}

export function retrieveEmbeddedList(
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/store/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ export const Subtitle = {
UPDATE_SUBTITLE_STYLE: 'UPDATE_SUBTITLE_STYLE',
UPDATE_SUBTITLE_SIZE: 'UPDATE_SUBTITLE_SIZE',
UPDATE_LAST_SUBTITLE_SIZE: 'UPDATE_LAST_SUBTITLE_SIZE',
UPDATE_NO_SUBTITLE: 'UPDATE_NO_SUBTITLE',
UPDATE_SUBTITLE_TOP: 'UPDATE_SUBTITLE_TOP',
REMOVE_LOCAL_SUBTITLE: 'REMOVE_LOCAL_SUBTITLE',
UPDATE_SUBTITLE_TYPE: 'UPDATE_SUBTITLE_TYPE',
Expand Down Expand Up @@ -74,6 +73,7 @@ export const SubtitleManager = {
removeSubtitle: 'REMOVE_SUBTITLE',
refreshSubtitlesInitially: 'REFRESH_SUBTITLES_INITIALLY',
refreshSubtitles: 'REFRESH_SUBTITLES',
refreshOnlineSubtitles: 'REFRESH_ONLINE_SUBTITLES',
addLocalSubtitles: 'ADD_LOCAL_SUBTITLES',
addLocalSubtitlesWithSelect: 'ADD_LOCAL_SUBTITLES_WITH_SELECT',
addEmbeddedSubtitles: 'ADD_EMBEDDED_SUBTITLES',
Expand Down
8 changes: 0 additions & 8 deletions src/renderer/store/modules/Subtitle/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ const state = {
lastChosenSize: 1,
subtitleDelay: 0,
scaleNum: 1,
calculatedNoSub: true,
subToTop: false,
isFirstSubtitle: true,
isPrimarySubSettings: true,
Expand Down Expand Up @@ -76,7 +75,6 @@ const getters = {
chosenSize: state => state.chosenSize,
lastChosenSize: state => state.lastChosenSize,
scaleNum: state => state.scaleNum,
calculatedNoSub: state => state.calculatedNoSub,
subToTop: state => state.subToTop,
isFirstSubtitle: state => state.isFirstSubtitle,
enabledSecondarySub: state => state.enabledSecondarySub,
Expand Down Expand Up @@ -150,9 +148,6 @@ const mutations = {
[subtitleMutations.SUBTITLE_SIZE_UPDATE](state, payload) {
state.chosenSize = payload;
},
[subtitleMutations.NO_SUBTITLE_UPDATE](state, payload) {
state.calculatedNoSub = payload;
},
[subtitleMutations.SUBTITLE_TOP_UPDATE](state, payload) {
state.subToTop = payload;
},
Expand Down Expand Up @@ -290,9 +285,6 @@ const actions = {
[subtitleActions.UPDATE_SUBTITLE_SIZE]({ commit }, delta) {
commit(subtitleMutations.SUBTITLE_SIZE_UPDATE, delta);
},
[subtitleActions.UPDATE_NO_SUBTITLE]({ commit }, delta) {
commit(subtitleMutations.NO_SUBTITLE_UPDATE, delta);
},
[subtitleActions.UPDATE_SUBTITLE_TOP]({ commit }, delta) {
commit(subtitleMutations.SUBTITLE_TOP_UPDATE, delta);
},
Expand Down
217 changes: 130 additions & 87 deletions src/renderer/store/modules/SubtitleManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const getters = {
},
primaryDelay({ primaryDelay }: SubtitleManagerState) { return primaryDelay; },
secondaryDelay({ secondaryDelay }: SubtitleManagerState) { return secondaryDelay; },
calculatedNoSub(state: any, { list }: any) { return !list.length; },
};
const mutations = {
[m.setPlaylistId](state: SubtitleManagerState, id: number) {
Expand Down Expand Up @@ -123,7 +124,7 @@ type AddSubtitleOptions = {
playlistId: number;
mediaItemId: string;
};
function privacyConfirm() {
function privacyConfirm(): Promise<boolean> {
const { $bus } = Vue.prototype;
$bus.$emit('privacy-confirm');
return new Promise((resolve) => {
Expand All @@ -138,7 +139,7 @@ function setDelayTimeout() {
clearTimeout(alterDelayTimeoutId);
alterDelayTimeoutId = setTimeout(() => store.dispatch(a.storeSubtitleDelays), 10000);
}
function fetchOnlineListWithErrorHandling(
function fetchOnlineListWithBubble(
videoSrc: string,
languageCode: LanguageCode,
hints?: string,
Expand Down Expand Up @@ -186,109 +187,143 @@ const actions = {
secondarySelectionComplete = false;
commit(m.setIsRefreshing, true);
dispatch(a.startAISelection);

const { playlistId, mediaItemId } = state;
const {
primaryLanguage, secondaryLanguage,
originSrc,
privacyAgreement,
} = getters;
const preference = await retrieveSubtitlePreference(playlistId, mediaItemId);
const { primaryLanguage, secondaryLanguage } = getters;
const needRefreshing = (
const hasStoredSubtitles = !!preference && !!preference.list.length;
const languageHasChanged = (
!preference ||
!preference.list.length ||
!!differenceWith(
Object.values(preference.language),
[primaryLanguage, secondaryLanguage],
).length
);
if (preference && !needRefreshing) {
try {
await Promise.race([
dispatch(a.addDatabaseSubtitles, {
storedList: preference.list,
selected: preference.selected,
playlistId, mediaItemId,
}),
new Promise((resolve, reject) => setTimeout(() => reject('timeout'), 10000)),
]);
} catch(error) {
console.error(error);
} finally {

if (hasStoredSubtitles && !languageHasChanged && preference) {
return Promise.race([
dispatch(a.addDatabaseSubtitles, {
storedList: preference.list,
selected: preference.selected,
playlistId, mediaItemId,
}),
new Promise((resolve, reject) => setTimeout(() => reject('timeout'), 10000)),
])
.catch(console.error)
.finally(() => {
commit(m.setIsRefreshing, false);
dispatch(legacyActions.UPDATE_SUBTITLE_TYPE, true);
dispatch(a.stopAISelection);
});
}

if (hasStoredSubtitles && preference) await dispatch(a.addDatabaseSubtitles, {
storedList: preference.list,
selected: preference.selected,
playlistId, mediaItemId,
});

let onlinePromise = Promise.resolve();
/** whether or not to refresh online subtitles */
let onlineNeeded = (languageHasChanged || !hasStoredSubtitles) && ['mkv', 'avi', 'ts', 'mp4'].includes(extname(originSrc).slice(1)) && privacyAgreement;
if (onlineNeeded) onlinePromise = dispatch(a.refreshOnlineSubtitles);
/** whether or not to refresh embedded subtitles */
const embeddedNeeded = getters.list.some(({ type }: SubtitleControlListItem) => type === Type.Embedded);
if (embeddedNeeded) retrieveEmbeddedList(originSrc).then((streams) => dispatch(a.addEmbeddedSubtitles, streams));

return Promise.race([
Promise.all([
onlinePromise,
dispatch(a.addLocalSubtitles, await searchForLocalList(originSrc)),
]),
new Promise((resolve, reject) => setTimeout(() => reject(new Error('timeout')), 10000)),
])
.catch(console.error)
.finally(() => {
dispatch(a.stopAISelection);
storeSubtitleLanguage([primaryLanguage, secondaryLanguage], playlistId, mediaItemId);
addSubtitleItemsToList(getters.list, playlistId, mediaItemId);
dispatch(a.checkLocalSubtitles);
dispatch(a.checkSubtitleList);
commit(m.setIsRefreshing, false);
dispatch(legacyActions.UPDATE_SUBTITLE_TYPE, true);
dispatch(a.stopAISelection);
}
} else if (!preference && needRefreshing) {
return dispatch(a.refreshSubtitles, { playlistId, mediaItemId, noOnline: !getters.privacyAgreement });
} else if (preference && needRefreshing) {
return dispatch(a.addDatabaseSubtitles, {
storedList: preference.list,
selected: preference.selected,
playlistId, mediaItemId,
})
.then(() => dispatch(a.refreshSubtitles, { playlistId, mediaItemId, noOnline: !getters.privacyAgreement }));
}
});
},
async [a.refreshSubtitles](
{ state, getters, dispatch, commit }: any,
args: { playlistId: number, mediaItemId: string, noOnline: boolean },
) {
const { playlistId, mediaItemId } = args || state;
async [a.refreshSubtitles]({ state, getters, dispatch, commit }: any) {
const { playlistId, mediaItemId } = state;
const {
originSrc,
primaryLanguage, secondaryLanguage,
privacyAgreement,
} = getters;

primarySelectionComplete = false;
secondarySelectionComplete = false;
commit(m.setIsRefreshing, true);
dispatch(a.startAISelection);
const { list } = getters as { list: SubtitleControlListItem[] };
const { originSrc, primaryLanguage, secondaryLanguage } = getters;
let onlinePromise = new Promise((resolve) => resolve());
/** do not serach online subtitles if extension is not one of mkv, ts, avi and mp4 */
let online = args && args.noOnline ? false : ['mkv', 'avi', 'ts', 'mp4'].includes(extname(originSrc).slice(1));
if (online && !getters.privacyAgreement) online = !!(await privacyConfirm());
if (online) {
addBubble(ONLINE_LOADING);
const hints = generateHints(originSrc);
onlinePromise = Promise.all([
fetchOnlineListWithErrorHandling(originSrc, primaryLanguage, hints),
fetchOnlineListWithErrorHandling(originSrc, secondaryLanguage, hints),
]).then((resultsList) => {
const results = flatten(resultsList);
const newSubtitlesToAdd: TranscriptInfo[] = [];
const oldSubtitlesToDel: SubtitleControlListItem[] = [];
const oldSubtitles = [...(getters as { list: SubtitleControlListItem[] }).list];
oldSubtitlesToDel.push(...remove(oldSubtitles, ({ type, language }) => type === Type.Online && language !== primaryLanguage && language !== secondaryLanguage));
oldSubtitlesToDel.push(...remove(oldSubtitles, ({ type, hash }) => type === Type.Online && !results.find(({ transcriptIdentity }) => transcriptIdentity === hash)));
newSubtitlesToAdd.push(...results.filter(({ transcriptIdentity }) => !oldSubtitles.find(({ id }) => id === transcriptIdentity)));
return { delete: oldSubtitlesToDel, add: newSubtitlesToAdd };
}).then((result) => dispatch(a.addOnlineSubtitles, { transcriptInfoList: result.add, playlistId, mediaItemId })
.then(() => dispatch(a.deleteSubtitlesByUuid, result.delete)));
}
/** whether to search embedded subtitles */
const embedded = list.some(({ type }) => type === Type.Embedded);
if (embedded) retrieveEmbeddedList(originSrc).then((streams) => dispatch(a.addEmbeddedSubtitles, { streams, playlistId, mediaItemId }));
try {
await Promise.race([
Promise.all([
onlinePromise,
dispatch(a.addLocalSubtitles, { paths: await searchForLocalList(originSrc), playlistId, mediaItemId }),
]),
new Promise((resolve, reject) => setTimeout(() => reject(new Error('timeout')), 10000)),
]);
dispatch(a.stopAISelection);
storeSubtitleLanguage([primaryLanguage, secondaryLanguage], playlistId, mediaItemId);
addSubtitleItemsToList(getters.list, playlistId, mediaItemId);
dispatch(a.checkLocalSubtitles);
} catch(ex) {
console.error(ex);
} finally {
dispatch(a.checkSubtitleList);
commit(m.setIsRefreshing, false);
dispatch(legacyActions.UPDATE_SUBTITLE_TYPE, true);
}
const onlineNeeded = privacyAgreement ? true : await privacyConfirm();
const onlinePromise = onlineNeeded ? dispatch(a.refreshOnlineSubtitles, true) : Promise.resolve();
return Promise.race([
Promise.all([
onlinePromise,
dispatch(a.addLocalSubtitles, await searchForLocalList(originSrc)),
]),
new Promise((resolve, reject) => setTimeout(() => reject(new Error('timeout')), 10000)),
])
.catch(console.error)
.finally(() => {
dispatch(a.stopAISelection);
storeSubtitleLanguage([primaryLanguage, secondaryLanguage], playlistId, mediaItemId);
addSubtitleItemsToList(getters.list, playlistId, mediaItemId);
dispatch(a.checkLocalSubtitles);
dispatch(a.checkSubtitleList);
commit(m.setIsRefreshing, false);
dispatch(legacyActions.UPDATE_SUBTITLE_TYPE, true);
});
},
async [a.refreshOnlineSubtitles]({ getters, dispatch }: any, bubble?: boolean) {
const {
originSrc,
primaryLanguage, secondaryLanguage,
} = getters;
if (bubble) addBubble(ONLINE_LOADING);
const hints = generateHints(originSrc);
return Promise.all(bubble ? [
fetchOnlineListWithBubble(originSrc, primaryLanguage, hints),
fetchOnlineListWithBubble(originSrc, secondaryLanguage, hints),
] : [
fetchOnlineList(originSrc, primaryLanguage, hints),
fetchOnlineList(originSrc, secondaryLanguage, hints),
]).then((resultsList) => {
const results = flatten(resultsList);
const newSubtitlesToAdd: TranscriptInfo[] = [];
const oldSubtitlesToDel: SubtitleControlListItem[] = [];
const oldSubtitles = [...(getters as { list: SubtitleControlListItem[] }).list];
// delete subtitles not matching the current language preference
oldSubtitlesToDel.push(...remove(oldSubtitles, ({ type, language }) => type === Type.Online && language !== primaryLanguage && language !== secondaryLanguage));
// delete subtitles not existed in the new subtitles
oldSubtitlesToDel.push(...remove(oldSubtitles, ({ type, hash }) => type === Type.Online && !results.find(({ transcriptIdentity }) => transcriptIdentity === hash)));
// add subtitles not existed in the old subtitles
newSubtitlesToAdd.push(...results.filter(({ transcriptIdentity }) => !oldSubtitles.find(({ id }) => id === transcriptIdentity)));
return { delete: oldSubtitlesToDel, add: newSubtitlesToAdd };
}).then((result) => dispatch(a.addOnlineSubtitles, result.add)
.then(() => dispatch(a.deleteSubtitlesByUuid, result.delete)));
},
[a.checkLocalSubtitles]({ dispatch, getters }: any) {
const localInvalidSubtitles = getters.list.filter(({ type, source }: any) => type === Type.Local && !existsSync(source));
if (localInvalidSubtitles.length) return dispatch(a.deleteSubtitlesByUuid, localInvalidSubtitles).then(() => addBubble(LOCAL_SUBTITLE_REMOVED));
},
async [a.addLocalSubtitles]({ dispatch }: any, { paths, playlistId, mediaItemId }: any) {
async [a.addLocalSubtitles]({ dispatch, state }: any, paths: string[]) {
return Promise.all(
paths.map((path: string) => dispatch(a.addSubtitle, { generator: new LocalGenerator(path), playlistId, mediaItemId }))
paths.map((path: string) => dispatch(a.addSubtitle, {
generator: new LocalGenerator(path),
playlistId: state.playlistId,
mediaItemId: state.mediaItemId,
}))
);
},
async [a.addLocalSubtitlesWithSelect]({ state, dispatch, getters }: any, paths: string[]) {
Expand Down Expand Up @@ -320,14 +355,22 @@ const actions = {
});
}
},
async [a.addEmbeddedSubtitles]({ dispatch }: any, { streams, playlistId, mediaItemId }: any) {
async [a.addEmbeddedSubtitles]({ dispatch, state }: any, streams: [string, ISubtitleStream][]) {
return Promise.all(
streams.map((stream: [string, ISubtitleStream]) => dispatch(a.addSubtitle, { generator: new EmbeddedGenerator(stream[0], stream[1]), playlistId, mediaItemId }))
streams.map((stream) => dispatch(a.addSubtitle, {
generator: new EmbeddedGenerator(stream[0], stream[1]),
playlistId: state.playlistId,
mediaItemId: state.mediaItemId,
}))
);
},
async [a.addOnlineSubtitles]({ dispatch }: any, { transcriptInfoList, playlistId, mediaItemId }: any) {
async [a.addOnlineSubtitles]({ dispatch, state }: any, transcriptInfoList: TranscriptInfo[]) {
return Promise.all(
transcriptInfoList.map((info: TranscriptInfo) => dispatch(a.addSubtitle, { generator: new OnlineGenerator(info), playlistId, mediaItemId }))
transcriptInfoList.map((info: TranscriptInfo) => dispatch(a.addSubtitle, {
generator: new OnlineGenerator(info),
playlistId: state.playlistId,
mediaItemId: state.mediaItemId,
}))
);
},
async [a.addDatabaseSubtitles]({ getters, dispatch }: any, options: AddDatabaseSubtitlesOptions) {
Expand Down
1 change: 0 additions & 1 deletion src/renderer/store/mutationTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ export const Subtitle = {
SUBTITLE_STYLE_UPDATE: 'SUBTITLE_STYLE_UPDATE',
SUBTITLE_SIZE_UPDATE: 'SUBTITLE_SIZE_UPDATE',
LAST_SUBTITLE_SIZE_UPDATE: 'LAST_SUBTITLE_SIZE_UPDATE',
NO_SUBTITLE_UPDATE: 'NO_SUBTITLE_UPDATE',
SUBTITLE_TOP_UPDATE: 'SUBTITLE_TOP_UPDATE',
CURRENT_SUBTITLE_REMOVE: 'CURRENT_SUBTITLE_REMOVE',
SUBTITLE_TYPE_UPDATE: 'SUBTITLE_TYPE_UPDATE',
Expand Down

0 comments on commit 7597c81

Please sign in to comment.