🍙 Refactor and bugfix

This commit is contained in:
Shiba 2022-01-09 23:01:04 +00:00
parent 610b47d1e4
commit 30f077ba38
5 changed files with 123 additions and 143 deletions

View file

@ -92,7 +92,7 @@ export function getChannelId(channelURL: string)
export function parseYouTubeURLTimeString(timeString: string) export function parseYouTubeURLTimeString(timeString: string)
{ {
const signs = timeString.replace(/[0-9]/g, '') const signs = timeString.replace(/[0-9]/g, '')
if (signs.length === 0) return 0 if (signs.length === 0) return null
const numbers = timeString.replace(/[^0-9]/g, '-').split('-') const numbers = timeString.replace(/[^0-9]/g, '-').split('-')
let total = 0 let total = 0
for (let i = 0; i < signs.length; i++) for (let i = 0; i < signs.length; i++)
@ -104,7 +104,7 @@ export function parseYouTubeURLTimeString(timeString: string)
case 'h': t *= 60 case 'h': t *= 60
case 'm': t *= 60 case 'm': t *= 60
case 's': break case 's': break
default: return 0 default: return null
} }
total += t total += t
} }

View file

@ -2,9 +2,6 @@
let db: IDBDatabase | null = null let db: IDBDatabase | null = null
// Throw if its not in the background
if (chrome.extension.getBackgroundPage() !== self) throw new Error()
if (typeof self.indexedDB !== 'undefined') if (typeof self.indexedDB !== 'undefined')
{ {
const openRequest = indexedDB.open("yt-url-resolver-cache") const openRequest = indexedDB.open("yt-url-resolver-cache")

View file

@ -17,31 +17,32 @@ export interface YtIdResolverDescriptor
*/ */
export async function resolveById(descriptors: YtIdResolverDescriptor[], progressCallback?: (progress: number) => void): Promise<(string | null)[]> export async function resolveById(descriptors: YtIdResolverDescriptor[], progressCallback?: (progress: number) => void): Promise<(string | null)[]>
{ {
const descriptorsWithIndex: (YtIdResolverDescriptor & { index: number })[] = descriptors.map((descriptor, index) => ({ ...descriptor, index })) let descriptorsPayload: (YtIdResolverDescriptor & { index: number })[] = descriptors.map((descriptor, index) => ({ ...descriptor, index }))
descriptors = null as any descriptors = null as any
const results: (string | null)[] = [] const results: (string | null)[] = [];
await Promise.all(descriptorsWithIndex.map(async (descriptor, index) =>
descriptorsPayload = (await Promise.all(descriptorsPayload.map(async (descriptor, index) =>
{ {
if (!descriptor) return if (!descriptor?.id) return
const cache = await LbryPathnameCache.get(descriptor.id) const cache = await LbryPathnameCache.get(descriptor.id)
// Cache can be null, if there is no lbry url yet // Cache can be null, if there is no lbry url yet
if (cache !== undefined) if (cache !== undefined)
{ {
// Directly setting it to results // Null values shouldn't be in the results
results[index] = cache if (cache) results[index] = cache
return
// We remove it so we dont ask it to API
descriptorsWithIndex.splice(index, 1)
} }
}))
const descriptorsChunks = chunk(descriptorsWithIndex, QUERY_CHUNK_SIZE) return descriptor
}))).filter((descriptor) => descriptor) as any
const descriptorsPayloadChunks = chunk(descriptorsPayload, QUERY_CHUNK_SIZE)
let progressCount = 0 let progressCount = 0
await Promise.all(descriptorsChunks.map(async (descriptorChunk) => await Promise.all(descriptorsPayloadChunks.map(async (descriptorChunk) =>
{ {
const descriptorsGroupedByType: Record<YtIdResolverDescriptor['type'], typeof descriptorsWithIndex | null> = groupBy(descriptorChunk, (descriptor) => descriptor.type) as any const descriptorsGroupedByType: Record<YtIdResolverDescriptor['type'], typeof descriptorsPayload | null> = groupBy(descriptorChunk, (descriptor) => descriptor.type) as any
const { urlResolver: urlResolverSettingName } = await getExtensionSettingsAsync() const { urlResolver: urlResolverSettingName } = await getExtensionSettingsAsync()
const urlResolverSetting = ytUrlResolversSettings[urlResolverSettingName] const urlResolverSetting = ytUrlResolversSettings[urlResolverSettingName]
@ -54,26 +55,18 @@ export interface YtIdResolverDescriptor
{ {
switch (typeof path) switch (typeof path)
{ {
case 'string': case 'string': case 'number': response = response[path]; continue
case 'number': }
response = response[path]
break
default:
switch (path) switch (path)
{ {
case Keys: case Keys: response = Object.keys(response); continue
response = Object.keys(response) case Values: response = Object.values(response); continue
break
case Values:
response = Object.values(response)
break
}
} }
} }
return response as T return response as T
} }
async function requestGroup(urlResolverFunction: YtUrlResolveFunction, descriptorsGroup: typeof descriptorsWithIndex) async function requestGroup(urlResolverFunction: YtUrlResolveFunction, descriptorsGroup: typeof descriptorsPayload)
{ {
url.pathname = urlResolverFunction.pathname url.pathname = urlResolverFunction.pathname
@ -81,45 +74,31 @@ export interface YtIdResolverDescriptor
{ {
await Promise.all(descriptorsGroup.map(async (descriptor) => await Promise.all(descriptorsGroup.map(async (descriptor) =>
{ {
switch (null)
{
default:
if (!descriptor.id) break
url.searchParams.set(urlResolverFunction.paramName, descriptor.id) url.searchParams.set(urlResolverFunction.paramName, descriptor.id)
const apiResponse = await fetch(url.toString(), { cache: 'no-store' }) const apiResponse = await fetch(url.toString(), { cache: 'no-store' })
if (!apiResponse.ok) if (apiResponse.ok)
{ {
// Some API might not respond with 200 if it can't find the url
if (apiResponse.status === 404) await LbryPathnameCache.put(null, descriptor.id)
break
}
const value = followResponsePath<string>(await apiResponse.json(), urlResolverFunction.responsePath) const value = followResponsePath<string>(await apiResponse.json(), urlResolverFunction.responsePath)
if (value) results[descriptor.index] = value if (value) results[descriptor.index] = value
await LbryPathnameCache.put(value, descriptor.id) await LbryPathnameCache.put(value, descriptor.id)
} }
else if (apiResponse.status === 404) await LbryPathnameCache.put(null, descriptor.id)
progressCount++ progressCount++
if (progressCallback) progressCallback(progressCount / descriptorsWithIndex.length) if (progressCallback) progressCallback(progressCount / descriptorsPayload.length)
})) }))
} }
else else
{ {
url.searchParams.set(urlResolverFunction.paramName, descriptorsGroup
switch (null)
{
default:
url.searchParams
.set(urlResolverFunction.paramName, descriptorsGroup
.map((descriptor) => descriptor.id) .map((descriptor) => descriptor.id)
.filter((descriptorId) => descriptorId) .join(urlResolverFunction.paramArraySeperator))
.join(urlResolverFunction.paramArraySeperator)
)
const apiResponse = await fetch(url.toString(), { cache: 'no-store' }) const apiResponse = await fetch(url.toString(), { cache: 'no-store' })
if (!apiResponse.ok) break if (apiResponse.ok)
{
const values = followResponsePath<string[]>(await apiResponse.json(), urlResolverFunction.responsePath) const values = followResponsePath<string[]>(await apiResponse.json(), urlResolverFunction.responsePath)
await Promise.all(values.map(async (value, index) => await Promise.all(values.map(async (value, index) =>
{ {
const descriptor = descriptorsGroup[index] const descriptor = descriptorsGroup[index]
@ -127,15 +106,15 @@ export interface YtIdResolverDescriptor
await LbryPathnameCache.put(value, descriptor.id) await LbryPathnameCache.put(value, descriptor.id)
})) }))
} }
progressCount += descriptorsGroup.length progressCount += descriptorsGroup.length
if (progressCallback) progressCallback(progressCount / descriptorsWithIndex.length) if (progressCallback) progressCallback(progressCount / descriptorsPayload.length)
} }
} }
if (descriptorsGroupedByType['channel']) await requestGroup(urlResolverSetting.functions.getChannelId, descriptorsGroupedByType['channel']) if (descriptorsGroupedByType['channel']) await requestGroup(urlResolverSetting.functions.getChannelId, descriptorsGroupedByType['channel'])
if (descriptorsGroupedByType['video']) await requestGroup(urlResolverSetting.functions.getVideoId, descriptorsGroupedByType['video']) if (descriptorsGroupedByType['video']) await requestGroup(urlResolverSetting.functions.getVideoId, descriptorsGroupedByType['video'])
})) }))
if (progressCallback) progressCallback(1);
return results return results
} }

View file

@ -54,8 +54,7 @@ export function WatchOnLbryButton({ targetPlatform, lbryPathname, time }: WatchO
function updateButton(mountPoint: HTMLDivElement, target: Target | null): void function updateButton(mountPoint: HTMLDivElement, target: Target | null): void
{ {
if (!target) return render(<WatchOnLbryButton />, mountPoint) if (!target) return render(<WatchOnLbryButton />, mountPoint)
const time = target.time && target.time > 3 ? target.time : null render(<WatchOnLbryButton targetPlatform={target.platfrom} lbryPathname={target.lbryPathname} time={target.time ?? undefined} />, mountPoint)
render(<WatchOnLbryButton targetPlatform={target.platfrom} lbryPathname={target.lbryPathname} time={time ?? undefined} />, mountPoint)
} }
function redirectTo({ lbryPathname, platfrom, time }: Target) function redirectTo({ lbryPathname, platfrom, time }: Target)
@ -112,8 +111,8 @@ window.addEventListener('load', async () =>
chrome.storage.onChanged.addListener(async (changes, areaName) => chrome.storage.onChanged.addListener(async (changes, areaName) =>
{ {
if (areaName !== 'local') return if (areaName !== 'local') return
Object.assign(settings, changes) Object.assign(settings, Object.fromEntries(Object.entries(changes).map(([key, change]) => [key, change.newValue])))
updateByURL(new URL(location.href)) await updateByURL(new URL(location.href))
}) })
/* /*
@ -127,6 +126,13 @@ window.addEventListener('load', async () =>
// We should get this from background, so the caching works and we don't get erros in the future if yt decides to impliment CORS // We should get this from background, so the caching works and we don't get erros in the future if yt decides to impliment CORS
const requestLbryPathname = async (videoId: string) => await new Promise<string | null>((resolve) => chrome.runtime.sendMessage({ videoId }, resolve)) const requestLbryPathname = async (videoId: string) => await new Promise<string | null>((resolve) => chrome.runtime.sendMessage({ videoId }, resolve))
function getVideoTime(url: URL)
{
return settings.redirect ?
(url.searchParams.has('t') ? parseYouTubeURLTimeString(url.searchParams.get('t')!) : null) :
(videoElement.currentTime > 3 && videoElement.currentTime < videoElement.duration - 1 ? videoElement.currentTime : null)
}
let target: Target | null = null let target: Target | null = null
async function updateByURL(url: URL) async function updateByURL(url: URL)
{ {
@ -136,15 +142,15 @@ window.addEventListener('load', async () =>
if (!videoId) return if (!videoId) return
const lbryPathname = await requestLbryPathname(videoId) const lbryPathname = await requestLbryPathname(videoId)
if (!lbryPathname) return if (!lbryPathname) return
const time = settings.redirect ? parseYouTubeURLTimeString(url.searchParams.get('t') ?? '0') : videoElement.currentTime const time = getVideoTime(url)
target = { lbryPathname, platfrom: targetPlatformSettings[settings.targetPlatform], time } target = { lbryPathname, platfrom: targetPlatformSettings[settings.targetPlatform], time }
if (settings.redirect) redirectTo(target) if (settings.redirect) redirectTo(target)
else updateButton(buttonMountPoint, target) else updateButton(buttonMountPoint, target)
} }
videoElement.addEventListener('timeupdate', () => target && updateButton(buttonMountPoint, Object.assign(target, { time: videoElement.currentTime }))) videoElement.addEventListener('timeupdate',
videoElement.addEventListener('ended', () => target && updateButton(buttonMountPoint, Object.assign(target, { time: null }))) () => target && updateButton(buttonMountPoint, Object.assign(target, { time: getVideoTime(new URL(location.href)) })))
async function onUrlChange() async function onUrlChange()
{ {

View file

@ -5,8 +5,6 @@ import { getFileContent, getSubsFromCsv, getSubsFromJson, getSubsFromOpml } from
import { resolveById } from '../common/yt/urlResolve' import { resolveById } from '../common/yt/urlResolve'
import readme from './README.md' import readme from './README.md'
/** /**
* Parses the subscription file and queries the API for lbry channels * Parses the subscription file and queries the API for lbry channels
* *