-
-
Notifications
You must be signed in to change notification settings - Fork 432
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added YouTube parser for thumbnail, content, snippet and other metadata. #297
base: master
Are you sure you want to change the base?
Changes from all commits
c1ddfad
52165b3
21eec93
3007083
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ import intl from "react-intl-universal" | |
import { urlTest, byteToMB, calculateItemSize, getSearchEngineName } from "../../scripts/utils" | ||
import { ThemeSettings, SearchEngines } from "../../schema-types" | ||
import { getThemeSettings, setThemeSettings, exportAll } from "../../scripts/settings" | ||
import { Stack, Label, Toggle, TextField, DefaultButton, ChoiceGroup, IChoiceGroupOption, loadTheme, Dropdown, IDropdownOption, PrimaryButton } from "@fluentui/react" | ||
import { Stack, Label, Toggle, TextField, DefaultButton, ChoiceGroup, IChoiceGroupOption, loadTheme, Dropdown, IDropdownOption, PrimaryButton, ToggleBase } from "@fluentui/react" | ||
import DangerButton from "../utils/danger-button" | ||
|
||
type AppTabProps = { | ||
|
@@ -20,6 +20,7 @@ type AppTabState = { | |
itemSize: string | ||
cacheSize: string | ||
deleteIndex: string | ||
iconStatus: boolean | ||
} | ||
|
||
class AppTab extends React.Component<AppTabProps, AppTabState> { | ||
|
@@ -31,7 +32,8 @@ class AppTab extends React.Component<AppTabProps, AppTabState> { | |
themeSettings: getThemeSettings(), | ||
itemSize: null, | ||
cacheSize: null, | ||
deleteIndex: null | ||
deleteIndex: null, | ||
iconStatus: window.settings.getIconStatus() | ||
} | ||
this.getItemSize() | ||
this.getCacheSize() | ||
|
@@ -73,6 +75,11 @@ class AppTab extends React.Component<AppTabProps, AppTabState> { | |
this.props.setFetchInterval(item.key as number) | ||
} | ||
|
||
toggleIcon = () => { | ||
window.settings.toggleIconStatus() | ||
this.setState({ iconStatus: window.settings.getIconStatus() }) | ||
} | ||
|
||
searchEngineOptions = (): IDropdownOption[] => [ | ||
SearchEngines.Google, SearchEngines.Bing, SearchEngines.Baidu, SearchEngines.DuckDuckGo | ||
].map(engine => ({ | ||
|
@@ -183,6 +190,13 @@ class AppTab extends React.Component<AppTabProps, AppTabState> { | |
</Stack.Item> | ||
</Stack> | ||
|
||
<Toggle | ||
label="Use custom icons when available" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All strings should be extracted for internationalization. |
||
checked={this.state.iconStatus} | ||
onText="Enabled" | ||
offText="Disabled" | ||
onChanged={this.toggleIcon} /> | ||
|
||
<Stack horizontal verticalAlign="baseline"> | ||
<Stack.Item grow> | ||
<Label>{intl.get("app.enableProxy")}</Label> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ const rssParser = new Parser({ | |
item: [ | ||
"thumb", "image", ["content:encoded", "fullContent"], | ||
['media:content', 'mediaContent', {keepArray: true}], | ||
['media:group', 'videoMeta', {keepArray: false}], | ||
] as Parser.CustomFieldItem[] | ||
} | ||
}) | ||
|
@@ -73,8 +74,33 @@ export async function parseRSS(url: string) { | |
|
||
export const domParser = new DOMParser() | ||
|
||
export async function fetchYTChannelIcon(url: string) { | ||
let channelId = url.split("=")[1] | ||
url = url.split("/").slice(0, 3).join("/") | ||
let channelUrl = url + '/channel/' + channelId | ||
let result = await fetch(channelUrl, { credentials: 'omit', }) | ||
if (result.ok) { | ||
let html = await result.text() | ||
let dom = domParser.parseFromString(html, "text/html") | ||
let links = dom.getElementsByTagName("link") | ||
for (let link of links) { | ||
let rel = link.getAttribute("rel") | ||
if (rel === "image_src" && link.hasAttribute("href")) { | ||
let href = link.getAttribute("href") | ||
return href.replace("=s900", "=s16") | ||
} | ||
} | ||
} | ||
} | ||
|
||
export async function fetchFavicon(url: string) { | ||
try { | ||
const customIcon = window.settings.getIconStatus() | ||
if (customIcon) { | ||
if (Url.parse(url).host === "www.youtube.com" && url.includes("channel")) { | ||
return fetchYTChannelIcon(url) | ||
} | ||
} | ||
url = url.split("/").slice(0, 3).join("/") | ||
let result = await fetch(url, { credentials: "omit" }) | ||
if (result.ok) { | ||
|
@@ -88,6 +114,7 @@ export async function fetchFavicon(url: string) { | |
let parsedUrl = Url.parse(url) | ||
if (href.startsWith("//")) return parsedUrl.protocol + href | ||
else if (href.startsWith("/")) return url + href | ||
else if (href.startsWith("favicon")) return url + '/' + href | ||
else return href | ||
} | ||
} | ||
|
@@ -121,6 +148,13 @@ export function htmlDecode(input: string) { | |
return doc.documentElement.textContent | ||
} | ||
|
||
export function parseYouTubeContent(url: string, views: string, likes: string, content: string){ | ||
const video = `<iframe width="560" height="315" src="${url}" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>` | ||
const views_likes = `<p style="float: left; color: #808080;"><em>${views} views</em></p><p style="float: right; color: #808080;"><em>${likes} likes</em></p>` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same internationalization suggestion here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. gkd 👍 |
||
const _content = `<div style="word-break: break-all;>"<pre>${content.replace(/(?:\r\n|\r|\n)/g, '<br>')}</pre></div>` | ||
return `${video}${views_likes}<br><br><br><hr />${_content}` | ||
} | ||
|
||
export const urlTest = (s: string) => | ||
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,63}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/gi.test(s) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we use a better variable name here and below for
iconStatus
that includes "youtube" to make this clearer?