mirror of
https://github.com/LBRYFoundation/Watch-on-LBRY.git
synced 2025-08-23 17:47:26 +00:00
Support YouTube takeout JSON
This commit is contained in:
parent
08842c4ba8
commit
bd5adf6652
2 changed files with 43 additions and 13 deletions
|
@ -6,12 +6,24 @@ const LBRY_API_HOST = 'https://api.lbry.com';
|
||||||
const QUERY_CHUNK_SIZE = 300;
|
const QUERY_CHUNK_SIZE = 300;
|
||||||
|
|
||||||
interface YtResolverResponse {
|
interface YtResolverResponse {
|
||||||
success: boolean
|
success: boolean;
|
||||||
error: object | null
|
error: object | null;
|
||||||
data: {
|
data: {
|
||||||
videos?: Record<string, string>
|
videos?: Record<string, string>;
|
||||||
channels?: Record<string, string>
|
channels?: Record<string, string>;
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface YtSubscription {
|
||||||
|
id: string;
|
||||||
|
etag: string;
|
||||||
|
title: string;
|
||||||
|
snippet: {
|
||||||
|
description: string;
|
||||||
|
resourceId: {
|
||||||
|
channelId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,15 +53,29 @@ export const ytService = {
|
||||||
* Reads the array of YT channels from an OPML file
|
* Reads the array of YT channels from an OPML file
|
||||||
*
|
*
|
||||||
* @param opmlContents an opml file as as tring
|
* @param opmlContents an opml file as as tring
|
||||||
* @returns the channel URLs
|
* @returns the channel IDs
|
||||||
*/
|
*/
|
||||||
readOpml(opmlContents: string): string[] {
|
readOpml(opmlContents: string): string[] {
|
||||||
const opml = new DOMParser().parseFromString(opmlContents, 'application/xml');
|
const opml = new DOMParser().parseFromString(opmlContents, 'application/xml');
|
||||||
|
|
||||||
return Array.from(opml.querySelectorAll('outline > outline'))
|
return Array.from(opml.querySelectorAll('outline > outline'))
|
||||||
.map(outline => outline.getAttribute('xmlUrl'))
|
.map(outline => outline.getAttribute('xmlUrl'))
|
||||||
|
.filter((url): url is string => !!url)
|
||||||
|
.map(url => ytService.getChannelId(url))
|
||||||
.filter((url): url is string => !!url); // we don't want it if it's empty
|
.filter((url): url is string => !!url); // we don't want it if it's empty
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads an array of YT channel IDs from the YT subscriptions JSON file
|
||||||
|
*
|
||||||
|
* @param jsonContents a JSON file as a string
|
||||||
|
* @returns the channel IDs
|
||||||
|
*/
|
||||||
|
readJson(jsonContents: string): string[] {
|
||||||
|
const subscriptions: YtSubscription[] = JSON.parse(jsonContents);
|
||||||
|
return subscriptions.map(sub => sub.snippet.resourceId.channelId);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts the channelID from a YT URL.
|
* Extracts the channelID from a YT URL.
|
||||||
*
|
*
|
||||||
|
@ -89,7 +115,7 @@ export const ytService = {
|
||||||
video_ids: groups['video']?.map(s => s.id).join(','),
|
video_ids: groups['video']?.map(s => s.id).join(','),
|
||||||
channel_ids: groups['channel']?.map(s => s.id).join(','),
|
channel_ids: groups['channel']?.map(s => s.id).join(','),
|
||||||
}));
|
}));
|
||||||
return fetch(`${LBRY_API_HOST}/yt/resolve?${params}`, {cache: 'force-cache'})
|
return fetch(`${LBRY_API_HOST}/yt/resolve?${params}`, { cache: 'force-cache' })
|
||||||
.then(rsp => rsp.ok ? rsp.json() : null);
|
.then(rsp => rsp.ok ? rsp.json() : null);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -5,16 +5,19 @@ import { getSettingsAsync, redirectDomains } from '../common/settings';
|
||||||
import { YTDescriptor, getFileContent, ytService } from '../common/yt';
|
import { YTDescriptor, getFileContent, ytService } from '../common/yt';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses OPML file and queries the API for lbry channels
|
* Parses the subscription file and queries the API for lbry channels
|
||||||
*
|
*
|
||||||
* @param file to read
|
* @param file to read
|
||||||
* @returns a promise with the list of channels that were found on lbry
|
* @returns a promise with the list of channels that were found on lbry
|
||||||
*/
|
*/
|
||||||
async function lbryChannelsFromOpml(file: File): Promise<string[]> {
|
async function lbryChannelsFromFile(file: File) {
|
||||||
const lbryUrls = await ytService.resolveById(...ytService.readOpml(await getFileContent(file))
|
const ext = file.name.split('.').pop()?.toLowerCase();
|
||||||
.map(url => ytService.getId(url))
|
const content = await getFileContent(file);
|
||||||
.filter((id): id is YTDescriptor => !!id));
|
|
||||||
|
|
||||||
|
const ids: YTDescriptor[] = (ext === 'xml' || ext == 'opml' ? ytService.readOpml(content) : ytService.readJson(content))
|
||||||
|
.map(id => ({ id, type: 'channel' }));
|
||||||
|
|
||||||
|
const lbryUrls = await ytService.resolveById(...ids);
|
||||||
const { redirect } = await getSettingsAsync('redirect');
|
const { redirect } = await getSettingsAsync('redirect');
|
||||||
const urlPrefix = redirectDomains[redirect].prefix;
|
const urlPrefix = redirectDomains[redirect].prefix;
|
||||||
return lbryUrls.map(channel => urlPrefix + channel);
|
return lbryUrls.map(channel => urlPrefix + channel);
|
||||||
|
@ -24,6 +27,7 @@ function YTtoLBRY() {
|
||||||
const [file, setFile] = useState(null as File | null);
|
const [file, setFile] = useState(null as File | null);
|
||||||
const [lbryChannels, setLbryChannels] = useState([] as string[]);
|
const [lbryChannels, setLbryChannels] = useState([] as string[]);
|
||||||
const [isLoading, setLoading] = useState(false);
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<iframe width="100%" height="400px" allowFullScreen
|
<iframe width="100%" height="400px" allowFullScreen
|
||||||
src="https://lbry.tv/$/embed/howtouseconverter/c9827448d6ac7a74ecdb972c5cdf9ddaf648a28e" />
|
src="https://lbry.tv/$/embed/howtouseconverter/c9827448d6ac7a74ecdb972c5cdf9ddaf648a28e" />
|
||||||
|
@ -35,7 +39,7 @@ function YTtoLBRY() {
|
||||||
<input type="button" value="Start Conversion!" class="goButton" disabled={!file || isLoading} onClick={async () => {
|
<input type="button" value="Start Conversion!" class="goButton" disabled={!file || isLoading} onClick={async () => {
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setLbryChannels(await lbryChannelsFromOpml(file));
|
setLbryChannels(await lbryChannelsFromFile(file));
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}} />
|
}} />
|
||||||
<ul>
|
<ul>
|
||||||
|
|
Loading…
Add table
Reference in a new issue