You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
3.1 KiB
121 lines
3.1 KiB
import map from 'lodash/map'
|
|
import last from 'lodash/last'
|
|
import uniq from 'lodash/uniq'
|
|
import filter from 'lodash/filter'
|
|
import reduce from 'lodash/reduce'
|
|
import concat from 'lodash/concat'
|
|
import orderBy from 'lodash/orderBy'
|
|
import isEmpty from 'lodash/isEmpty'
|
|
import groupBy from 'lodash/groupBy'
|
|
|
|
import type {
|
|
Videos,
|
|
PlaylistData,
|
|
Episode,
|
|
} from 'requests'
|
|
|
|
import type { Chapters, Urls } from 'features/MultiSourcePlayer/types'
|
|
|
|
const getUniquePeriods = (videos: Videos) => uniq(map(videos, ({ period }) => period))
|
|
|
|
type Video = {
|
|
duration: number,
|
|
period: number,
|
|
urls: Urls,
|
|
}
|
|
|
|
const getVideoByPeriod = (videos: Videos, period: number) => {
|
|
const videosWithSamePeriod = filter(videos, { period })
|
|
if (isEmpty(videosWithSamePeriod)) return null
|
|
|
|
const urls = reduce(
|
|
videosWithSamePeriod,
|
|
(acc: Urls, video) => ({
|
|
...acc,
|
|
[video.quality]: video.url,
|
|
}),
|
|
{},
|
|
)
|
|
const [video] = videosWithSamePeriod
|
|
return {
|
|
duration: video.duration,
|
|
period: video.period,
|
|
urls,
|
|
}
|
|
}
|
|
|
|
const getVideoByPeriods = (videos: Videos, periods: Array<number>) => (
|
|
reduce(
|
|
periods,
|
|
(acc: Array<Video>, period) => {
|
|
const video = getVideoByPeriod(videos, period)
|
|
return video ? concat(acc, video) : acc
|
|
},
|
|
[],
|
|
)
|
|
)
|
|
|
|
const getFullMatchChapters = (videos: Array<Video>) => {
|
|
const sortedVideos = orderBy(videos, ({ period }) => period)
|
|
return reduce(
|
|
sortedVideos,
|
|
(acc: Chapters, video) => {
|
|
const prevVideoEndMs = last(acc)?.endMs || 0
|
|
const nextChapter = {
|
|
duration: video.duration,
|
|
endMs: prevVideoEndMs + video.duration,
|
|
period: video.period,
|
|
startMs: prevVideoEndMs,
|
|
startOffsetMs: 0,
|
|
urls: video.urls,
|
|
}
|
|
return concat(acc, nextChapter)
|
|
},
|
|
[],
|
|
)
|
|
}
|
|
|
|
const getEpisodeUrls = (urls: Urls, episode: Episode) => reduce(
|
|
urls,
|
|
(
|
|
acc: Urls,
|
|
url,
|
|
qulaity,
|
|
) => {
|
|
acc[qulaity] = `${url}#t=${episode.s},${episode.e}`
|
|
return acc
|
|
},
|
|
{},
|
|
)
|
|
|
|
const getPlaylistChapters = (videos: Array<Video>, playlists: PlaylistData) => {
|
|
const groupedByPeriods = groupBy(videos, ({ period }) => period)
|
|
return reduce(
|
|
playlists,
|
|
(acc: Chapters, episode) => {
|
|
const video = groupedByPeriods[episode.h]?.[0]
|
|
if (!video || episode.s >= episode.e) return acc
|
|
|
|
const episodeDuration = (episode.e - episode.s) * 1000
|
|
const prevVideoEndMs = last(acc)?.endMs || 0
|
|
const nextChapter = {
|
|
duration: episodeDuration,
|
|
endMs: prevVideoEndMs + episodeDuration,
|
|
period: video.period,
|
|
startMs: prevVideoEndMs,
|
|
startOffsetMs: episode.s * 1000,
|
|
urls: getEpisodeUrls(video.urls, episode),
|
|
}
|
|
return concat(acc, nextChapter)
|
|
},
|
|
[],
|
|
)
|
|
}
|
|
|
|
export const buildChapters = (videos: Videos, playlists: PlaylistData) => {
|
|
const periods = getUniquePeriods(videos)
|
|
const highQualityVideos = getVideoByPeriods(videos, periods)
|
|
return isEmpty(playlists)
|
|
? getFullMatchChapters(highQualityVideos)
|
|
: getPlaylistChapters(highQualityVideos, playlists)
|
|
}
|
|
|