mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Add some scrolling on youtube videos
This commit is contained in:
parent
2dbd16a61e
commit
7c3d15e31a
3 changed files with 138 additions and 0 deletions
88
packages/api/src/jobs/process-youtube-video.ts
Normal file
88
packages/api/src/jobs/process-youtube-video.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import { logger } from '../utils/logger'
|
||||
import { authTrx } from '../repository'
|
||||
import { libraryItemRepository } from '../repository/library_item'
|
||||
import { LibraryItem, LibraryItemState } from '../entity/library_item'
|
||||
|
||||
import { Video, Client as YouTubeClient } from 'youtubei'
|
||||
|
||||
export interface ProcessYouTubeVideoJobData {
|
||||
userId: string
|
||||
libraryItemId: string
|
||||
}
|
||||
|
||||
export const PROCESS_YOUTUBE_VIDEO_JOB_NAME = 'process-youtube-video'
|
||||
|
||||
const calculateWordCount = (durationInSeconds: number): number => {
|
||||
// Calculate word count using the formula: word count = read time (in seconds) * words per second
|
||||
// Assuming average reading speed is 235 words per minute (or about 3.92 words per second)
|
||||
const wordsPerSecond = 3.92
|
||||
const wordCount = Math.round(durationInSeconds * wordsPerSecond)
|
||||
return wordCount
|
||||
}
|
||||
|
||||
export const processYouTubeVideo = async (
|
||||
jobData: ProcessYouTubeVideoJobData
|
||||
) => {
|
||||
try {
|
||||
const libraryItem = await authTrx(
|
||||
async (tx) =>
|
||||
tx
|
||||
.withRepository(libraryItemRepository)
|
||||
.findById(jobData.libraryItemId),
|
||||
undefined,
|
||||
jobData.userId
|
||||
)
|
||||
if (!libraryItem || libraryItem.state !== LibraryItemState.Succeeded) {
|
||||
logger.info(
|
||||
`Not ready to get YouTube metadata job state: ${
|
||||
libraryItem?.state ?? 'null'
|
||||
}`
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const u = new URL(libraryItem.originalUrl)
|
||||
const videoId = u.searchParams.get('v')
|
||||
|
||||
if (!videoId) {
|
||||
console.warn('no video id for supplied youtube url', {
|
||||
url: libraryItem.originalUrl,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
let needsUpdate = false
|
||||
const youtube = new YouTubeClient()
|
||||
const video = await youtube.getVideo(videoId)
|
||||
if (!video) {
|
||||
console.warn('no video found for youtube url', {
|
||||
url: libraryItem.originalUrl,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (video.description && libraryItem.description !== video.description) {
|
||||
needsUpdate = true
|
||||
libraryItem.description = video.description
|
||||
}
|
||||
|
||||
if ('duration' in video && (video as Video).duration > 0) {
|
||||
needsUpdate = true
|
||||
libraryItem.wordCount = calculateWordCount((video as Video).duration)
|
||||
}
|
||||
|
||||
if (needsUpdate) {
|
||||
const _ = await authTrx(
|
||||
async (t) => {
|
||||
return t
|
||||
.getRepository(LibraryItem)
|
||||
.update(jobData.libraryItemId, libraryItem)
|
||||
},
|
||||
undefined,
|
||||
jobData.userId
|
||||
)
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('error creating summary: ', err)
|
||||
}
|
||||
}
|
||||
|
|
@ -115,6 +115,28 @@ export function Article(props: ArticleProps): JSX.Element {
|
|||
}
|
||||
}, 2500)
|
||||
|
||||
useEffect(() => {
|
||||
const youtubePlayer = document.getElementById('_omnivore_youtube_video')
|
||||
|
||||
const updateScroll = () => {
|
||||
console.log('scroll y: ', window.scrollY, youtubePlayer)
|
||||
|
||||
if (youtubePlayer) {
|
||||
if (window.scrollY > 200) {
|
||||
youtubePlayer.classList.add('is-sticky')
|
||||
} else {
|
||||
youtubePlayer.classList.remove('is-sticky')
|
||||
}
|
||||
}
|
||||
}
|
||||
if (youtubePlayer) {
|
||||
window.addEventListener('scroll', updateScroll)
|
||||
}
|
||||
return () => {
|
||||
window.removeEventListener('scroll', updateScroll) // clean up
|
||||
}
|
||||
}, [props])
|
||||
|
||||
// Scroll to initial anchor position
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
|
|
|
|||
|
|
@ -610,3 +610,31 @@
|
|||
white-space: pre-wrap;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.is-sticky {
|
||||
position: fixed;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
top: auto;
|
||||
left: auto;
|
||||
max-width: 400px;
|
||||
max-height: 222px;
|
||||
width: 400px;
|
||||
height: 222px;
|
||||
animation-name: fadeInUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue