Skip to content
This repository has been archived by the owner on Dec 27, 2024. It is now read-only.

Commit

Permalink
chore: add subtitle button
Browse files Browse the repository at this point in the history
  • Loading branch information
sircharlo committed Jun 13, 2024
1 parent 53bde02 commit bf24dd2
Show file tree
Hide file tree
Showing 14 changed files with 128 additions and 46 deletions.
9 changes: 8 additions & 1 deletion src/components/media/MediaDisplayButton.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<template>
<!-- :color="mediaPlayer.windowVisible ? '' : 'red-5'" -->
<q-btn
:color="mediaPlayer.windowVisible ? '' : 'red-5'"
:disable="!!disabled"
:flat="!disabled"
:icon="mediaPlayer.windowVisible ? 'mdi-television' : 'mdi-television-off'"
:outline="!!disabled"
@click="mediaDisplayPopup = false"
round
size="md"
v-if="currentSettings?.enableMediaDisplayButton"
>
Expand Down Expand Up @@ -45,6 +46,12 @@
</q-card-actions>
</q-card>
</q-popup-proxy>
<q-badge
:color="mediaPlayer.windowVisible ? 'positive' : 'negative'"
floating
rounded
style="margin-top: 1.5em"
/>
</q-btn>
</template>

Expand Down
27 changes: 27 additions & 0 deletions src/components/media/SubtitlesButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<template>
<q-btn
@click="mediaPlayer.subtitlesVisible = !mediaPlayer.subtitlesVisible"
flat
icon="mdi-subtitles"
round
v-if="currentSettings.enableSubtitles"
>
<q-tooltip>{{ $t('subtitles') }}</q-tooltip>
<q-badge
:color="mediaPlayer.subtitlesVisible ? 'positive' : 'negative'"
floating
rounded
style="margin-top: 1.5em"
/>
</q-btn>
</template>

<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { useCurrentStateStore } from '../../stores/current-state';
// Initialize store and destructure reactive properties
const currentState = useCurrentStateStore();
const { currentSettings, mediaPlayer } = storeToRefs(currentState);
</script>
4 changes: 2 additions & 2 deletions src/defaults/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export const settingsDefinitions: SettingsItems = {
list: 'jwLanguages',
type: 'list',
},
langSubs: {
langSubtitles: {
depends: 'enableSubtitles',
group: 'mediaRetrievalPlayback',
list: 'jwLanguages',
Expand Down Expand Up @@ -378,7 +378,7 @@ export const defaultSettings: SettingsValues = {
langFallback: '',
// mediaWinShortcut: 'Alt+Z',
// musicFadeOutTime: 60,
langSubs: '',
langSubtitles: '',
localAppLang: 'en-US',
maxRes: '720p',
// musicFadeOutType: 'smart',
Expand Down
46 changes: 46 additions & 0 deletions src/helpers/fs.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Item } from 'klaw-sync';
import { electronApi } from 'src/helpers/electron-api';
import { useCurrentStateStore } from 'src/stores/current-state';
import { PublicationFetcher } from 'src/types/publications';
import { MultimediaItem } from 'src/types/sqlite';

import { FULL_HD } from './converters';
import { downloadFileIfNeeded, getJwMediaInfo } from './jw-media';
import { isImage, isVideo } from './mediaPlayback';

const { fs, getUserDataPath, klawSync, path } = electronApi;
Expand Down Expand Up @@ -150,11 +153,54 @@ const getThumbnailUrl = async (filepath: string) => {
return thumbnailUrl;
};

const getSubtitlesUrl = async (multimediaItem: MultimediaItem) => {
const currentState = useCurrentStateStore();
const { getSettingValue } = currentState;
if (!getSettingValue('enableSubtitles')) return '';
let subtitlesUrl = '';
if (
isVideo(multimediaItem.FilePath) &&
multimediaItem.KeySymbol &&
multimediaItem.Track
) {
let subtitlesPath = multimediaItem.FilePath.split('.')[0] + '.vtt';
const subtitleLang = getSettingValue('langSubtitles') as string;
const subtitleFetcher: PublicationFetcher = {
fileformat: 'mp4',
issue: multimediaItem.IssueTagNumber,
langwritten: subtitleLang ?? (getSettingValue('lang') as string),
pub: multimediaItem.KeySymbol,
track: multimediaItem.Track,
};
const { subtitles } = await getJwMediaInfo(subtitleFetcher);
if (!subtitles) return '';
const subtitlesFilename = path.basename(
multimediaItem.FilePath.split('.')[0] + '.vtt',
);
const subDirectory = getPublicationDirectory(subtitleFetcher);
await downloadFileIfNeeded({
dir: subDirectory,
filename: subtitlesFilename,
url: subtitles,
});
subtitlesPath = path.join(subDirectory, subtitlesFilename);
if (fs.existsSync(subtitlesPath)) {
subtitlesUrl = getFileUrl(subtitlesPath);
} else {
return '';
}
} else {
return '';
}
return subtitlesUrl;
};

export {
getDurationFromMediaPath,
getFileUrl,
getPublicationDirectory,
getPublicationDirectoryContents,
getSubtitlesUrl,
getTempDirectory,
getThumbnailUrl,
};
62 changes: 25 additions & 37 deletions src/helpers/jw-media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
getDurationFromMediaPath,
getFileUrl,
getPublicationDirectory,
getSubtitlesUrl,
getThumbnailUrl,
} from './fs';
import {
Expand Down Expand Up @@ -139,13 +140,8 @@ const fetchMedia = async () => {
let fetchResult = null;
if (day.meeting === 'we') {
fetchResult = await getWeMedia(dayDate);
// day.dynamicMedia = weMedia.media;
//formatDate
// if (weMedia.error) fetchErrors[date.formatDate(dayDate, 'YYYYMMDD')] = weMedia.error;
} else if (day.meeting === 'mw') {
fetchResult = await getMwMedia(dayDate);
// day.dynamicMedia = mwMedia.media;
// if (mwMedia.error) fetchErrors[date.formatDate(dayDate, 'YYYYMMDD')] = mwMedia.error;
}
if (fetchResult) {
day.dynamicMedia = fetchResult.media;
Expand Down Expand Up @@ -555,6 +551,7 @@ const dynamicMediaMapper = async (
paragraph: m.TargetParagraphNumberLabel,
section, // if is we: wt; else, if >= middle song: LAC; >= (middle song - 8???): AYFM; else: TGW
song: mediaIsSong,
subtitlesUrl: await getSubtitlesUrl(m),
thumbnailUrl,
title: mediaIsSong ? m.Label.replace(/^\d+\.\s*/, '') : m.Label,
uniqueId: sanitizeId(
Expand Down Expand Up @@ -872,12 +869,9 @@ const getMwMedia = async (lookupDate: Date) => {
if (mepsLang) media.AlternativeLanguage = mepsLang;
}
}
console.log('multimediaMepsLangs', multimediaMepsLangs);
await processMissingMediaInfo(allMedia);
console.log('allMedia', allMedia);

const dynamicMediaForDay = await dynamicMediaMapper(allMedia, lookupDate);
console.log('dynamicMediaForDay', dynamicMediaForDay);

const dynamicMedia: Record<
string,
Expand Down Expand Up @@ -949,17 +943,6 @@ async function processMissingMediaInfo(allMedia: MultimediaItem[]) {
(await getJwMediaInfo(publicationFetcher)).title;
}
}
// const publicationFetcher = {
// fileformat: media.MimeType?.includes('audio') ? 'MP3' : 'MP4',
// issue: media.IssueTagNumber,
// langwritten,
// pub: media.KeySymbol,
// ...(typeof media.Track === 'number' &&
// media.Track > 0 && { track: media.Track }),
// };
// if (!media.FilePath || !fs.existsSync(media.FilePath)) {
// media.FilePath = await downloadMissingMedia(publicationFetcher);
// }
}
}

Expand Down Expand Up @@ -1075,21 +1058,22 @@ const downloadMissingMedia = async (publication: PublicationFetcher) => {
url: bestItem.file.url,
})) as DownloadedFile;

// Save thumbnail if available but not present on disk
const { thumbnail } = await getJwMediaInfo(publication);
const thumbnailFilename =
path.basename(bestItem.file.url).split('.')[0] + path.extname(thumbnail);
if (
thumbnail &&
bestItem.file?.url &&
(downloadedFile?.new ||
!fs.existsSync(path.join(pubDir, thumbnailFilename)))
) {
await downloadFileIfNeeded({
dir: pubDir,
filename: thumbnailFilename,
url: thumbnail,
});
const jwMediaInfo = await getJwMediaInfo(publication);
for (const itemUrl of [jwMediaInfo.subtitles, jwMediaInfo.thumbnail]) {
if (!itemUrl) continue;
const itemFilename =
path.basename(bestItem.file.url).split('.')[0] + path.extname(itemUrl);
if (
itemUrl &&
bestItem.file?.url &&
(downloadedFile?.new || !fs.existsSync(path.join(pubDir, itemFilename)))
) {
await downloadFileIfNeeded({
dir: pubDir,
filename: itemFilename,
url: itemUrl,
});
}
}
return downloadedFile?.path;
};
Expand Down Expand Up @@ -1125,18 +1109,21 @@ const getJwMediaInfo = async (publication: PublicationFetcher) => {
if (issue && issue.endsWith('00')) issue = issue.slice(0, -2);
if (issue && issue !== '0') url += '_' + issue;
if (publication.track) url += '_' + publication.track;
if (publication.fileformat?.includes('MP4')) url += '_VIDEO';
else if (publication.fileformat?.includes('MP3')) url += '_AUDIO';
if (publication.fileformat?.toLowerCase().includes('mp4')) url += '_VIDEO';
else if (publication.fileformat?.toLowerCase().includes('mp3'))
url += '_AUDIO';
const responseObject = await get(url);
if (!responseObject.media || responseObject.media.length === 0)
throw new Error('No thumbnail found:' + url);
return {
subtitles:
findBestResolution(responseObject.media[0].files)?.subtitles?.url ?? '',
thumbnail: getBestImageUrl(responseObject.media[0].images),
title: responseObject.media[0].title,
};
} catch (error) {
console.error(error);
return { thumbnail: '', title: '' };
return { subtitles: '', thumbnail: '', title: '' };
}
};

Expand Down Expand Up @@ -1283,6 +1270,7 @@ export {
dynamicMediaMapper,
fetchMedia,
getDocumentMultimediaItems,
getJwMediaInfo,
getMwMedia,
getPubMediaLinks,
getPublicationInfoFromDb,
Expand Down
3 changes: 2 additions & 1 deletion src/i18n/en-US/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,5 +129,6 @@
"errorDownloadingMeetingMedia": "Error downloading media for meeting",
"tryConfiguringFallbackLanguage": "Try configuring a fallback language in the settings",
"entireFile": "Play from start to finish",
"sureStopVideo": "Are you sure you want to stop the video?"
"sureStopVideo": "Are you sure you want to stop the video?",
"subtitles": "Subtitles"
}
3 changes: 2 additions & 1 deletion src/i18n/fr-CA/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -294,5 +294,6 @@
"errorDownloadingMeetingMedia": "Erreur lors du téléchargement des médias pour la réunion",
"tryConfiguringFallbackLanguage": "Essayez de configurer une langue secondaire dans les paramètres",
"entireFile": "Jouez du début à la fin",
"sureStopVideo": "Êtes-vous sûr de vouloir arrêter la vidéo ?"
"sureStopVideo": "Êtes-vous sûr de vouloir arrêter la vidéo ?",
"subtitles": "Sous-titres"
}
6 changes: 4 additions & 2 deletions src/layouts/MainLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,15 @@
<q-space />
<ScenePicker />
<MusicButton />
<q-separator
<SubtitlesButton />
<!-- <q-separator
inset
v-if="
currentSettings?.enableMediaDisplayButton &&
currentSettings?.enableMusicButton
"
vertical
/>
/> -->
<MediaDisplayButton />
</q-toolbar>
</q-footer>
Expand Down Expand Up @@ -249,6 +250,7 @@ import MusicButton from 'src/components/media/MusicButton.vue';
import ObsStatus from 'src/components/media/ObsStatus.vue';
import ScenePicker from 'src/components/media/ScenePicker.vue';
import SongPicker from 'src/components/media/SongPicker.vue';
import SubtitlesButton from 'src/components/media/SubtitlesButton.vue';
import { getLookupPeriod } from 'src/helpers/date';
import { ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
Expand Down
2 changes: 2 additions & 0 deletions src/pages/MediaCalendarPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@ import { electronApi } from 'src/helpers/electron-api';
import {
getDurationFromMediaPath,
getFileUrl,
// getSubtitlesUrl,
getTempDirectory,
getThumbnailUrl,
} from 'src/helpers/fs';
Expand Down Expand Up @@ -847,6 +848,7 @@ const copyToDatedAdditionalMedia = async (files: string[]) => {
isImage: isImage(datedAdditionalMediaPath),
isVideo: isVideoFile,
section: 'additional',
// subtitlesUrl: await getSubtitlesUrl(datedAdditionalMediaPath),
thumbnailUrl: await getThumbnailUrl(datedAdditionalMediaPath),
title: path.basename(datedAdditionalMediaPath),
uniqueId: sanitizeId(
Expand Down
2 changes: 1 addition & 1 deletion src/pages/SettingsPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
/>
<SelectInput
:options="item.list"
:use-input="settingId === 'lang'"
:use-input="settingId === 'lang' || settingId === 'langFallback' || settingId === 'langSubtitles'"
v-else-if="item.type === 'list'"
v-model="currentSettings[settingId as keyof SettingsItems] as string"
/>
Expand Down
2 changes: 2 additions & 0 deletions src/stores/current-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ export const useCurrentStateStore = defineStore('current-state', {
currentPosition: 0,
scale: 1,
seekTo: 0,
subtitlesUrl: '',
subtitlesVisible: false,
uniqueId: '',
url: '',
windowVisible: true,
Expand Down
1 change: 1 addition & 0 deletions src/types/media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface DynamicMediaObject {
paragraph?: number;
section: string;
song?: boolean | string;
subtitlesUrl?: string;
thumbnailUrl: string;
title: string;
uniqueId: string;
Expand Down
5 changes: 5 additions & 0 deletions src/types/publications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ export interface MediaLink {
specialty: string;
specialtyDescr: string;
subtitled: boolean;
subtitles: {
checksum: string;
modifiedDatetime: string;
url: string;
};
title: string;
track: number;
trackImage: {
Expand Down
2 changes: 1 addition & 1 deletion src/types/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export interface SettingsValues {
langFallback: string;
// mediaWinShortcut: string;
// musicFadeOutTime: number;
langSubs: string;
langSubtitles: string;
localAppLang: string;
maxRes: string;
// musicFadeOutType: string;
Expand Down

0 comments on commit bf24dd2

Please sign in to comment.