Move calculateColorLuminance, calculatePublishedDate and buildVTTFileLocally out of the store (#2692)
This commit is contained in:
		
							parent
							
								
									7ca6440a88
								
							
						
					
					
						commit
						22a5062334
					
				|  | @ -11,6 +11,7 @@ import { MAIN_PROFILE_ID } from '../../../constants' | |||
| import fs from 'fs' | ||||
| import { opmlToJSON } from 'opml-to-json' | ||||
| import ytch from 'yt-channel-info' | ||||
| import { calculateColorLuminance } from '../../helpers/utils' | ||||
| 
 | ||||
| // FIXME: Missing web logic branching
 | ||||
| 
 | ||||
|  | @ -1092,7 +1093,7 @@ export default Vue.extend({ | |||
|           let index = convertedData.findIndex(p => p.name === profile.value) | ||||
|           if (index === -1) { // profile doesn't exist yet
 | ||||
|             const randomBgColor = await this.getRandomColor() | ||||
|             const contrastyTextColor = await this.calculateColorLuminance(randomBgColor) | ||||
|             const contrastyTextColor = calculateColorLuminance(randomBgColor) | ||||
|             convertedData.push({ | ||||
|               name: profile.value, | ||||
|               bgColor: randomBgColor, | ||||
|  | @ -1241,7 +1242,6 @@ export default Vue.extend({ | |||
|       'compactHistory', | ||||
|       'showToast', | ||||
|       'getRandomColor', | ||||
|       'calculateColorLuminance', | ||||
|       'showOpenDialog', | ||||
|       'readFileFromDialog', | ||||
|       'showSaveDialog', | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue' | |||
| import FtInput from '../../components/ft-input/ft-input.vue' | ||||
| import FtButton from '../../components/ft-button/ft-button.vue' | ||||
| import { MAIN_PROFILE_ID } from '../../../constants' | ||||
| import { calculateColorLuminance } from '../../helpers/utils' | ||||
| 
 | ||||
| export default Vue.extend({ | ||||
|   name: 'FtProfileEdit', | ||||
|  | @ -70,8 +71,8 @@ export default Vue.extend({ | |||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
|     profileBgColor: async function (val) { | ||||
|       this.profileTextColor = await this.calculateColorLuminance(val) | ||||
|     profileBgColor: function (val) { | ||||
|       this.profileTextColor = calculateColorLuminance(val) | ||||
|     } | ||||
|   }, | ||||
|   created: function () { | ||||
|  | @ -163,8 +164,7 @@ export default Vue.extend({ | |||
|       'updateProfile', | ||||
|       'removeProfile', | ||||
|       'updateDefaultProfile', | ||||
|       'updateActiveProfile', | ||||
|       'calculateColorLuminance' | ||||
|       'updateActiveProfile' | ||||
|     ]) | ||||
|   } | ||||
| }) | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ import 'videojs-http-source-selector' | |||
| 
 | ||||
| import { IpcChannels } from '../../../constants' | ||||
| import { sponsorBlockSkipSegments } from '../../helpers/sponsorblock' | ||||
| import { calculateColorLuminance } from '../../helpers/utils' | ||||
| 
 | ||||
| export default Vue.extend({ | ||||
|   name: 'FtVideoPlayer', | ||||
|  | @ -1175,7 +1176,7 @@ export default Vue.extend({ | |||
|       videojs.registerComponent('loopButton', loopButton) | ||||
|     }, | ||||
| 
 | ||||
|     toggleVideoLoop: async function () { | ||||
|     toggleVideoLoop: function () { | ||||
|       const loopButton = document.getElementById('loopButton') | ||||
| 
 | ||||
|       if (!this.player.loop()) { | ||||
|  | @ -1187,7 +1188,7 @@ export default Vue.extend({ | |||
|           return color === currentTheme | ||||
|         }) | ||||
| 
 | ||||
|         const themeTextColor = await this.calculateColorLuminance(colorValues[nameIndex]) | ||||
|         const themeTextColor = calculateColorLuminance(colorValues[nameIndex]) | ||||
| 
 | ||||
|         loopButton.classList.add('vjs-icon-loop-active') | ||||
| 
 | ||||
|  | @ -1923,7 +1924,6 @@ export default Vue.extend({ | |||
|     }, | ||||
| 
 | ||||
|     ...mapActions([ | ||||
|       'calculateColorLuminance', | ||||
|       'updateDefaultCaptionSettings', | ||||
|       'showToast', | ||||
|       'parseScreenshotCustomFileName', | ||||
|  |  | |||
|  | @ -0,0 +1,103 @@ | |||
| export function calculateColorLuminance(colorValue) { | ||||
|   const cutHex = colorValue.substring(1, 7) | ||||
|   const colorValueR = parseInt(cutHex.substring(0, 2), 16) | ||||
|   const colorValueG = parseInt(cutHex.substring(2, 4), 16) | ||||
|   const colorValueB = parseInt(cutHex.substring(4, 6), 16) | ||||
| 
 | ||||
|   const luminance = (0.299 * colorValueR + 0.587 * colorValueG + 0.114 * colorValueB) / 255 | ||||
| 
 | ||||
|   if (luminance > 0.5) { | ||||
|     return '#000000' | ||||
|   } else { | ||||
|     return '#FFFFFF' | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export function calculatePublishedDate(publishedText) { | ||||
|   const date = new Date() | ||||
|   if (publishedText === 'Live') { | ||||
|     return publishedText | ||||
|   } | ||||
| 
 | ||||
|   const textSplit = publishedText.split(' ') | ||||
| 
 | ||||
|   if (textSplit[0].toLowerCase() === 'streamed') { | ||||
|     textSplit.shift() | ||||
|   } | ||||
| 
 | ||||
|   const timeFrame = textSplit[1] | ||||
|   const timeAmount = parseInt(textSplit[0]) | ||||
|   let timeSpan = null | ||||
| 
 | ||||
|   if (timeFrame.indexOf('second') > -1) { | ||||
|     timeSpan = timeAmount * 1000 | ||||
|   } else if (timeFrame.indexOf('minute') > -1) { | ||||
|     timeSpan = timeAmount * 60000 | ||||
|   } else if (timeFrame.indexOf('hour') > -1) { | ||||
|     timeSpan = timeAmount * 3600000 | ||||
|   } else if (timeFrame.indexOf('day') > -1) { | ||||
|     timeSpan = timeAmount * 86400000 | ||||
|   } else if (timeFrame.indexOf('week') > -1) { | ||||
|     timeSpan = timeAmount * 604800000 | ||||
|   } else if (timeFrame.indexOf('month') > -1) { | ||||
|     timeSpan = timeAmount * 2592000000 | ||||
|   } else if (timeFrame.indexOf('year') > -1) { | ||||
|     timeSpan = timeAmount * 31556952000 | ||||
|   } | ||||
| 
 | ||||
|   return date.getTime() - timeSpan | ||||
| } | ||||
| 
 | ||||
| export function buildVTTFileLocally(storyboard) { | ||||
|   let vttString = 'WEBVTT\n\n' | ||||
|   // how many images are in one image
 | ||||
|   const numberOfSubImagesPerImage = storyboard.sWidth * storyboard.sHeight | ||||
|   // the number of storyboard images
 | ||||
|   const numberOfImages = Math.ceil(storyboard.count / numberOfSubImagesPerImage) | ||||
|   const intervalInSeconds = storyboard.interval / 1000 | ||||
|   let currentUrl = storyboard.url | ||||
|   let startHours = 0 | ||||
|   let startMinutes = 0 | ||||
|   let startSeconds = 0 | ||||
|   let endHours = 0 | ||||
|   let endMinutes = 0 | ||||
|   let endSeconds = intervalInSeconds | ||||
|   for (let i = 0; i < numberOfImages; i++) { | ||||
|     let xCoord = 0 | ||||
|     let yCoord = 0 | ||||
|     for (let j = 0; j < numberOfSubImagesPerImage; j++) { | ||||
|       // add the timestamp information
 | ||||
|       const paddedStartHours = startHours.toString().padStart(2, '0') | ||||
|       const paddedStartMinutes = startMinutes.toString().padStart(2, '0') | ||||
|       const paddedStartSeconds = startSeconds.toString().padStart(2, '0') | ||||
|       const paddedEndHours = endHours.toString().padStart(2, '0') | ||||
|       const paddedEndMinutes = endMinutes.toString().padStart(2, '0') | ||||
|       const paddedEndSeconds = endSeconds.toString().padStart(2, '0') | ||||
|       vttString += `${paddedStartHours}:${paddedStartMinutes}:${paddedStartSeconds}.000 --> ${paddedEndHours}:${paddedEndMinutes}:${paddedEndSeconds}.000\n` | ||||
|       // add the current image url as well as the x, y, width, height information
 | ||||
|       vttString += currentUrl + `#xywh=${xCoord},${yCoord},${storyboard.width},${storyboard.height}\n\n` | ||||
|       // update the variables
 | ||||
|       startHours = endHours | ||||
|       startMinutes = endMinutes | ||||
|       startSeconds = endSeconds | ||||
|       endSeconds += intervalInSeconds | ||||
|       if (endSeconds >= 60) { | ||||
|         endSeconds -= 60 | ||||
|         endMinutes += 1 | ||||
|       } | ||||
|       if (endMinutes >= 60) { | ||||
|         endMinutes -= 60 | ||||
|         endHours += 1 | ||||
|       } | ||||
|       // x coordinate can only be smaller than the width of one subimage * the number of subimages per row
 | ||||
|       xCoord = (xCoord + storyboard.width) % (storyboard.width * storyboard.sWidth) | ||||
|       // only if the x coordinate is , so in a new row, we have to update the y coordinate
 | ||||
|       if (xCoord === 0) { | ||||
|         yCoord += storyboard.height | ||||
|       } | ||||
|     } | ||||
|     // make sure that there is no value like M0 or M1 in the parameters that gets replaced
 | ||||
|     currentUrl = currentUrl.replace('M' + i.toString() + '.jpg', 'M' + (i + 1).toString() + '.jpg') | ||||
|   } | ||||
|   return vttString | ||||
| } | ||||
|  | @ -1,5 +1,6 @@ | |||
| import { MAIN_PROFILE_ID } from '../../../constants' | ||||
| import { DBProfileHandlers } from '../../../datastores/handlers/index' | ||||
| import { calculateColorLuminance } from '../../helpers/utils' | ||||
| 
 | ||||
| const state = { | ||||
|   profileList: [{ | ||||
|  | @ -53,7 +54,7 @@ const actions = { | |||
|     if (profiles.length === 0) { | ||||
|       // Create a default profile and persist it
 | ||||
|       const randomColor = await dispatch('getRandomColor') | ||||
|       const textColor = await dispatch('calculateColorLuminance', randomColor) | ||||
|       const textColor = calculateColorLuminance(randomColor) | ||||
|       const defaultProfile = { | ||||
|         _id: MAIN_PROFILE_ID, | ||||
|         name: defaultName, | ||||
|  |  | |||
|  | @ -563,57 +563,6 @@ const actions = { | |||
|     commit('setRegionValues', regionValues) | ||||
|   }, | ||||
| 
 | ||||
|   calculateColorLuminance (_, colorValue) { | ||||
|     const cutHex = colorValue.substring(1, 7) | ||||
|     const colorValueR = parseInt(cutHex.substring(0, 2), 16) | ||||
|     const colorValueG = parseInt(cutHex.substring(2, 4), 16) | ||||
|     const colorValueB = parseInt(cutHex.substring(4, 6), 16) | ||||
| 
 | ||||
|     const luminance = (0.299 * colorValueR + 0.587 * colorValueG + 0.114 * colorValueB) / 255 | ||||
| 
 | ||||
|     if (luminance > 0.5) { | ||||
|       return '#000000' | ||||
|     } else { | ||||
|       return '#FFFFFF' | ||||
|     } | ||||
|   }, | ||||
| 
 | ||||
|   calculatePublishedDate(_, publishedText) { | ||||
|     const date = new Date() | ||||
| 
 | ||||
|     if (publishedText === 'Live') { | ||||
|       return publishedText | ||||
|     } | ||||
| 
 | ||||
|     const textSplit = publishedText.split(' ') | ||||
| 
 | ||||
|     if (textSplit[0].toLowerCase() === 'streamed') { | ||||
|       textSplit.shift() | ||||
|     } | ||||
| 
 | ||||
|     const timeFrame = textSplit[1] | ||||
|     const timeAmount = parseInt(textSplit[0]) | ||||
|     let timeSpan = null | ||||
| 
 | ||||
|     if (timeFrame.indexOf('second') > -1) { | ||||
|       timeSpan = timeAmount * 1000 | ||||
|     } else if (timeFrame.indexOf('minute') > -1) { | ||||
|       timeSpan = timeAmount * 60000 | ||||
|     } else if (timeFrame.indexOf('hour') > -1) { | ||||
|       timeSpan = timeAmount * 3600000 | ||||
|     } else if (timeFrame.indexOf('day') > -1) { | ||||
|       timeSpan = timeAmount * 86400000 | ||||
|     } else if (timeFrame.indexOf('week') > -1) { | ||||
|       timeSpan = timeAmount * 604800000 | ||||
|     } else if (timeFrame.indexOf('month') > -1) { | ||||
|       timeSpan = timeAmount * 2592000000 | ||||
|     } else if (timeFrame.indexOf('year') > -1) { | ||||
|       timeSpan = timeAmount * 31556952000 | ||||
|     } | ||||
| 
 | ||||
|     return date.getTime() - timeSpan | ||||
|   }, | ||||
| 
 | ||||
|   getVideoParamsFromUrl (_, url) { | ||||
|     /** @type {URL} */ | ||||
|     let urlObject | ||||
|  | @ -855,86 +804,6 @@ const actions = { | |||
|     } | ||||
|   }, | ||||
| 
 | ||||
|   padNumberWithLeadingZeros(_, payload) { | ||||
|     let numberString = payload.number.toString() | ||||
|     while (numberString.length < payload.length) { | ||||
|       numberString = '0' + numberString | ||||
|     } | ||||
|     return numberString | ||||
|   }, | ||||
| 
 | ||||
|   async buildVTTFileLocally ({ dispatch }, Storyboard) { | ||||
|     let vttString = 'WEBVTT\n\n' | ||||
|     // how many images are in one image
 | ||||
|     const numberOfSubImagesPerImage = Storyboard.sWidth * Storyboard.sHeight | ||||
|     // the number of storyboard images
 | ||||
|     const numberOfImages = Math.ceil(Storyboard.count / numberOfSubImagesPerImage) | ||||
|     const intervalInSeconds = Storyboard.interval / 1000 | ||||
|     let currentUrl = Storyboard.url | ||||
|     let startHours = 0 | ||||
|     let startMinutes = 0 | ||||
|     let startSeconds = 0 | ||||
|     let endHours = 0 | ||||
|     let endMinutes = 0 | ||||
|     let endSeconds = intervalInSeconds | ||||
|     for (let i = 0; i < numberOfImages; i++) { | ||||
|       let xCoord = 0 | ||||
|       let yCoord = 0 | ||||
|       for (let j = 0; j < numberOfSubImagesPerImage; j++) { | ||||
|         // add the timestamp information
 | ||||
|         const paddedStartHours = await dispatch('padNumberWithLeadingZeros', { | ||||
|           number: startHours, | ||||
|           length: 2 | ||||
|         }) | ||||
|         const paddedStartMinutes = await dispatch('padNumberWithLeadingZeros', { | ||||
|           number: startMinutes, | ||||
|           length: 2 | ||||
|         }) | ||||
|         const paddedStartSeconds = await dispatch('padNumberWithLeadingZeros', { | ||||
|           number: startSeconds, | ||||
|           length: 2 | ||||
|         }) | ||||
|         const paddedEndHours = await dispatch('padNumberWithLeadingZeros', { | ||||
|           number: endHours, | ||||
|           length: 2 | ||||
|         }) | ||||
|         const paddedEndMinutes = await dispatch('padNumberWithLeadingZeros', { | ||||
|           number: endMinutes, | ||||
|           length: 2 | ||||
|         }) | ||||
|         const paddedEndSeconds = await dispatch('padNumberWithLeadingZeros', { | ||||
|           number: endSeconds, | ||||
|           length: 2 | ||||
|         }) | ||||
|         vttString += `${paddedStartHours}:${paddedStartMinutes}:${paddedStartSeconds}.000 --> ${paddedEndHours}:${paddedEndMinutes}:${paddedEndSeconds}.000\n` | ||||
|         // add the current image url as well as the x, y, width, height information
 | ||||
|         vttString += currentUrl + `#xywh=${xCoord},${yCoord},${Storyboard.width},${Storyboard.height}\n\n` | ||||
|         // update the variables
 | ||||
|         startHours = endHours | ||||
|         startMinutes = endMinutes | ||||
|         startSeconds = endSeconds | ||||
|         endSeconds += intervalInSeconds | ||||
|         if (endSeconds >= 60) { | ||||
|           endSeconds -= 60 | ||||
|           endMinutes += 1 | ||||
|         } | ||||
|         if (endMinutes >= 60) { | ||||
|           endMinutes -= 60 | ||||
|           endHours += 1 | ||||
|         } | ||||
|         // x coordinate can only be smaller than the width of one subimage * the number of subimages per row
 | ||||
|         xCoord = (xCoord + Storyboard.width) % (Storyboard.width * Storyboard.sWidth) | ||||
|         // only if the x coordinate is , so in a new row, we have to update the y coordinate
 | ||||
|         if (xCoord === 0) { | ||||
|           yCoord += Storyboard.height | ||||
|         } | ||||
|       } | ||||
|       // make sure that there is no value like M0 or M1 in the parameters that gets replaced
 | ||||
|       currentUrl = currentUrl.replace('M' + i.toString() + '.jpg', 'M' + (i + 1).toString() + '.jpg') | ||||
|     } | ||||
|     return vttString | ||||
|   }, | ||||
| 
 | ||||
|   toLocalePublicationString ({ dispatch }, payload) { | ||||
|     if (payload.isLive) { | ||||
|       return '0' + payload.liveStreamString | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ import FtProfileEdit from '../../components/ft-profile-edit/ft-profile-edit.vue' | |||
| import FtProfileChannelList from '../../components/ft-profile-channel-list/ft-profile-channel-list.vue' | ||||
| import FtProfileFilterChannelsList from '../../components/ft-profile-filter-channels-list/ft-profile-filter-channels-list.vue' | ||||
| import { MAIN_PROFILE_ID } from '../../../constants' | ||||
| import { calculateColorLuminance } from '../../helpers/utils' | ||||
| 
 | ||||
| export default Vue.extend({ | ||||
|   name: 'ProfileEdit', | ||||
|  | @ -60,7 +61,7 @@ export default Vue.extend({ | |||
|     if (profileType === 'newProfile') { | ||||
|       this.isNew = true | ||||
|       const bgColor = await this.getRandomColor() | ||||
|       const textColor = await this.calculateColorLuminance(bgColor) | ||||
|       const textColor = calculateColorLuminance(bgColor) | ||||
|       this.profile = { | ||||
|         name: '', | ||||
|         bgColor: bgColor, | ||||
|  | @ -88,8 +89,7 @@ export default Vue.extend({ | |||
|   methods: { | ||||
|     ...mapActions([ | ||||
|       'showToast', | ||||
|       'getRandomColor', | ||||
|       'calculateColorLuminance' | ||||
|       'getRandomColor' | ||||
|     ]) | ||||
|   } | ||||
| }) | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ import FtChannelBubble from '../../components/ft-channel-bubble/ft-channel-bubbl | |||
| import ytch from 'yt-channel-info' | ||||
| import Parser from 'rss-parser' | ||||
| import { MAIN_PROFILE_ID } from '../../../constants' | ||||
| import { calculatePublishedDate } from '../../helpers/utils' | ||||
| 
 | ||||
| export default Vue.extend({ | ||||
|   name: 'Subscriptions', | ||||
|  | @ -254,20 +255,20 @@ export default Vue.extend({ | |||
| 
 | ||||
|     getChannelVideosLocalScraper: function (channel, failedAttempts = 0) { | ||||
|       return new Promise((resolve, reject) => { | ||||
|         ytch.getChannelVideos({ channelId: channel.id, sortBy: 'latest' }).then(async (response) => { | ||||
|         ytch.getChannelVideos({ channelId: channel.id, sortBy: 'latest' }).then((response) => { | ||||
|           if (response.alertMessage) { | ||||
|             this.errorChannels.push(channel) | ||||
|             resolve([]) | ||||
|             return | ||||
|           } | ||||
|           const videos = await Promise.all(response.items.map(async (video) => { | ||||
|           const videos = response.items.map((video) => { | ||||
|             if (video.liveNow) { | ||||
|               video.publishedDate = new Date().getTime() | ||||
|             } else { | ||||
|               video.publishedDate = await this.calculatePublishedDate(video.publishedText) | ||||
|               video.publishedDate = calculatePublishedDate(video.publishedText) | ||||
|             } | ||||
|             return video | ||||
|           })) | ||||
|           }) | ||||
| 
 | ||||
|           resolve(videos) | ||||
|         }).catch((err) => { | ||||
|  | @ -497,7 +498,6 @@ export default Vue.extend({ | |||
|       'updateShowProgressBar', | ||||
|       'updateProfileSubscriptions', | ||||
|       'updateAllSubscriptionsList', | ||||
|       'calculatePublishedDate', | ||||
|       'copyToClipboard' | ||||
|     ]), | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ import WatchVideoPlaylist from '../../components/watch-video-playlist/watch-vide | |||
| import WatchVideoRecommendations from '../../components/watch-video-recommendations/watch-video-recommendations.vue' | ||||
| import FtAgeRestricted from '../../components/ft-age-restricted/ft-age-restricted.vue' | ||||
| import i18n from '../../i18n/index' | ||||
| import { buildVTTFileLocally } from '../../helpers/utils' | ||||
| 
 | ||||
| const isDev = process.env.NODE_ENV === 'development' | ||||
| 
 | ||||
|  | @ -1347,8 +1348,8 @@ export default Vue.extend({ | |||
|         }) | ||||
|       }) | ||||
|       // TODO: MAKE A VARIABLE WHICH CAN CHOOSE BETWEEN STORYBOARD ARRAY ELEMENTS
 | ||||
|       this.buildVTTFileLocally(storyboardArray[1]).then(async (results) => { | ||||
|         const userData = await this.getUserDataPath() | ||||
|       const results = buildVTTFileLocally(storyboardArray[1]) | ||||
|       this.getUserDataPath().then((userData) => { | ||||
|         let fileLocation | ||||
|         let uriSchema | ||||
| 
 | ||||
|  | @ -1536,7 +1537,6 @@ export default Vue.extend({ | |||
| 
 | ||||
|     ...mapActions([ | ||||
|       'showToast', | ||||
|       'buildVTTFileLocally', | ||||
|       'updateHistory', | ||||
|       'updateWatchProgress', | ||||
|       'getUserDataPath', | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue