@@ -13,17 +13,22 @@ import com.github.libretube.api.obj.Subtitle
1313import com.github.libretube.extensions.toID
1414import com.github.libretube.helpers.PlayerHelper
1515import com.github.libretube.ui.dialogs.ShareDialog.Companion.YOUTUBE_FRONTEND_URL
16+ import com.github.libretube.util.deArrow
17+ import kotlinx.coroutines.Dispatchers
18+ import kotlinx.coroutines.async
19+ import kotlinx.coroutines.withContext
1620import kotlinx.datetime.toKotlinInstant
21+ import org.schabi.newpipe.extractor.stream.AudioStream
1722import org.schabi.newpipe.extractor.stream.StreamInfo
1823import org.schabi.newpipe.extractor.stream.StreamInfoItem
1924import org.schabi.newpipe.extractor.stream.VideoStream
2025import retrofit2.HttpException
2126import java.io.IOException
2227
23- fun VideoStream.toPipedStream (): PipedStream = PipedStream (
28+ fun VideoStream.toPipedStream () = PipedStream (
2429 url = content,
2530 codec = codec,
26- format = format.toString(),
31+ format = format? .toString(),
2732 height = height,
2833 width = width,
2934 quality = getResolution(),
@@ -37,14 +42,34 @@ fun VideoStream.toPipedStream(): PipedStream = PipedStream(
3742 contentLength = itagItem?.contentLength ? : 0L
3843)
3944
45+ fun AudioStream.toPipedStream () = PipedStream (
46+ url = content,
47+ format = format?.toString(),
48+ quality = " $averageBitrate bits" ,
49+ bitrate = bitrate,
50+ mimeType = format?.mimeType,
51+ initStart = initStart,
52+ initEnd = initEnd,
53+ indexStart = indexStart,
54+ indexEnd = indexEnd,
55+ contentLength = itagItem?.contentLength ? : 0L ,
56+ codec = codec,
57+ audioTrackId = audioTrackId,
58+ audioTrackName = audioTrackName,
59+ audioTrackLocale = audioLocale?.toLanguageTag(),
60+ audioTrackType = audioTrackType?.name,
61+ videoOnly = false
62+ )
63+
4064fun StreamInfoItem.toStreamItem (
4165 uploaderAvatarUrl : String? = null
42- ): StreamItem = StreamItem (
66+ ) = StreamItem (
4367 type = StreamItem .TYPE_STREAM ,
4468 url = url.toID(),
4569 title = name,
4670 uploaded = uploadDate?.offsetDateTime()?.toEpochSecond()?.times(1000 ) ? : - 1 ,
47- uploadedDate = textualUploadDate ? : uploadDate?.offsetDateTime()?.toLocalDateTime()?.toLocalDate()
71+ uploadedDate = textualUploadDate ? : uploadDate?.offsetDateTime()?.toLocalDateTime()
72+ ?.toLocalDate()
4873 ?.toString(),
4974 uploaderName = uploaderName,
5075 uploaderUrl = uploaderUrl.toID(),
@@ -58,13 +83,22 @@ fun StreamInfoItem.toStreamItem(
5883)
5984
6085object StreamsExtractor {
61- suspend fun extractStreams (videoId : String ): Streams {
86+ suspend fun extractStreams (videoId : String ): Streams = withContext( Dispatchers . IO ) {
6287 if (! PlayerHelper .disablePipedProxy || ! PlayerHelper .localStreamExtraction) {
63- return RetrofitInstance .api.getStreams(videoId)
88+ return @withContext RetrofitInstance .api.getStreams(videoId).deArrow (videoId)
6489 }
6590
66- val resp = StreamInfo .getInfo(" ${YOUTUBE_FRONTEND_URL } /watch?v=$videoId " )
67- return Streams (
91+ val respAsync = async {
92+ StreamInfo .getInfo(" $YOUTUBE_FRONTEND_URL /watch?v=$videoId " )
93+ }
94+ val dislikesAsync = async {
95+ if (PlayerHelper .localRYD) runCatching {
96+ RetrofitInstance .externalApi.getVotes(videoId).dislikes
97+ }.getOrElse { - 1 } else - 1
98+ }
99+ val (resp, dislikes) = Pair (respAsync.await(), dislikesAsync.await())
100+
101+ Streams (
68102 title = resp.name,
69103 description = resp.description.content,
70104 uploader = resp.uploaderName,
@@ -75,9 +109,7 @@ object StreamsExtractor {
75109 category = resp.category,
76110 views = resp.viewCount,
77111 likes = resp.likeCount,
78- dislikes = if (PlayerHelper .localRYD) runCatching {
79- RetrofitInstance .externalApi.getVotes(videoId).dislikes
80- }.getOrElse { - 1 } else - 1 ,
112+ dislikes = dislikes,
81113 license = resp.licence,
82114 hls = resp.hlsUrl,
83115 dash = resp.dashMpdUrl,
@@ -95,39 +127,19 @@ object StreamsExtractor {
95127 uploadTimestamp = resp.uploadDate.offsetDateTime().toInstant().toKotlinInstant(),
96128 uploaded = resp.uploadDate.offsetDateTime().toEpochSecond() * 1000 ,
97129 thumbnailUrl = resp.thumbnails.maxBy { it.height }.url,
98- relatedStreams = resp.relatedItems.filterIsInstance<StreamInfoItem >().map(StreamInfoItem ::toStreamItem),
130+ relatedStreams = resp.relatedItems
131+ .filterIsInstance<StreamInfoItem >()
132+ .map { item -> item.toStreamItem() },
99133 chapters = resp.streamSegments.map {
100134 ChapterSegment (
101135 title = it.title,
102136 image = it.previewUrl.orEmpty(),
103137 start = it.startTimeSeconds.toLong()
104138 )
105139 },
106- audioStreams = resp.audioStreams.map {
107- PipedStream (
108- url = it.content,
109- format = it.format?.toString(),
110- quality = " ${it.averageBitrate} bits" ,
111- bitrate = it.bitrate,
112- mimeType = it.format?.mimeType,
113- initStart = it.initStart,
114- initEnd = it.initEnd,
115- indexStart = it.indexStart,
116- indexEnd = it.indexEnd,
117- contentLength = it.itagItem?.contentLength ? : 0L ,
118- codec = it.codec,
119- audioTrackId = it.audioTrackId,
120- audioTrackName = it.audioTrackName,
121- audioTrackLocale = it.audioLocale?.toLanguageTag(),
122- audioTrackType = it.audioTrackType?.name,
123- videoOnly = false
124- )
125- },
126- videoStreams = resp.videoOnlyStreams.map {
127- it.toPipedStream().copy(videoOnly = true )
128- } + resp.videoStreams.map {
129- it.toPipedStream().copy(videoOnly = false )
130- },
140+ audioStreams = resp.audioStreams.map { it.toPipedStream() },
141+ videoStreams = resp.videoOnlyStreams.map { it.toPipedStream().copy(videoOnly = true ) } +
142+ resp.videoStreams.map { it.toPipedStream().copy(videoOnly = false ) },
131143 previewFrames = resp.previewFrames.map {
132144 PreviewFrames (
133145 it.urls,
@@ -148,7 +160,7 @@ object StreamsExtractor {
148160 it.isAutoGenerated
149161 )
150162 }
151- )
163+ ).deArrow(videoId)
152164 }
153165
154166 fun getExtractorErrorMessageString (context : Context , exception : Exception ): String {
@@ -157,6 +169,7 @@ object StreamsExtractor {
157169 is HttpException -> exception.response()?.errorBody()?.string()?.runCatching {
158170 JsonHelper .json.decodeFromString<Message >(this ).message
159171 }?.getOrNull() ? : context.getString(R .string.server_error)
172+
160173 else -> exception.localizedMessage.orEmpty()
161174 }
162175 }
0 commit comments