|
20 | 20 |
|
21 | 21 | package org.schabi.newpipe.extractor.services.youtube.extractors; |
22 | 22 |
|
| 23 | +import static org.schabi.newpipe.extractor.localization.TimeAgoPatternsManager.getTimeAgoParserFor; |
23 | 24 | import static org.schabi.newpipe.extractor.services.youtube.ItagItem.APPROX_DURATION_MS_UNKNOWN; |
24 | 25 | import static org.schabi.newpipe.extractor.services.youtube.ItagItem.CONTENT_LENGTH_UNKNOWN; |
25 | 26 | import static org.schabi.newpipe.extractor.services.youtube.YoutubeDescriptionHelper.attributedDescriptionToHtml; |
|
59 | 60 | import org.schabi.newpipe.extractor.localization.DateWrapper; |
60 | 61 | import org.schabi.newpipe.extractor.localization.Localization; |
61 | 62 | import org.schabi.newpipe.extractor.localization.TimeAgoParser; |
62 | | -import org.schabi.newpipe.extractor.localization.TimeAgoPatternsManager; |
63 | 63 | import org.schabi.newpipe.extractor.services.youtube.ItagItem; |
64 | 64 | import org.schabi.newpipe.extractor.services.youtube.PoTokenProvider; |
65 | 65 | import org.schabi.newpipe.extractor.services.youtube.PoTokenResult; |
|
87 | 87 | import java.io.IOException; |
88 | 88 | import java.nio.charset.StandardCharsets; |
89 | 89 | import java.time.LocalDate; |
| 90 | +import java.time.OffsetDateTime; |
90 | 91 | import java.time.ZoneId; |
91 | 92 | import java.time.format.DateTimeFormatter; |
| 93 | +import java.time.format.DateTimeParseException; |
92 | 94 | import java.util.ArrayList; |
93 | 95 | import java.util.Arrays; |
94 | 96 | import java.util.Collections; |
95 | 97 | import java.util.List; |
96 | 98 | import java.util.Locale; |
97 | 99 | import java.util.Objects; |
| 100 | +import java.util.Optional; |
98 | 101 | import java.util.stream.Collectors; |
99 | 102 |
|
100 | 103 | import javax.annotation.Nonnull; |
@@ -169,77 +172,72 @@ public String getName() throws ParsingException { |
169 | 172 | @Nullable |
170 | 173 | @Override |
171 | 174 | public String getTextualUploadDate() throws ParsingException { |
172 | | - if (!playerMicroFormatRenderer.getString("uploadDate", "").isEmpty()) { |
173 | | - return playerMicroFormatRenderer.getString("uploadDate"); |
174 | | - } else if (!playerMicroFormatRenderer.getString("publishDate", "").isEmpty()) { |
175 | | - return playerMicroFormatRenderer.getString("publishDate"); |
| 175 | + final var uploadDate = getUploadDate(); |
| 176 | + if (uploadDate == null) { |
| 177 | + return null; |
176 | 178 | } |
| 179 | + return LocalDate.ofInstant(uploadDate.getInstant(), ZoneId.systemDefault()).toString(); |
| 180 | + } |
| 181 | + |
| 182 | + @Override |
| 183 | + public DateWrapper getUploadDate() throws ParsingException { |
| 184 | + final String dateStr = playerMicroFormatRenderer.getString("uploadDate", |
| 185 | + playerMicroFormatRenderer.getString("publishDate", "")); |
| 186 | + if (!dateStr.isEmpty()) { |
| 187 | + return new DateWrapper(OffsetDateTime.parse(dateStr)); |
| 188 | + } |
| 189 | + |
| 190 | + final var liveDetails = playerMicroFormatRenderer.getObject("liveBroadcastDetails"); |
| 191 | + final String timestamp = liveDetails.getString("endTimestamp", // an ended live stream |
| 192 | + liveDetails.getString("startTimestamp", "")); // a running live stream |
177 | 193 |
|
178 | | - final JsonObject liveDetails = playerMicroFormatRenderer.getObject( |
179 | | - "liveBroadcastDetails"); |
180 | | - if (!liveDetails.getString("endTimestamp", "").isEmpty()) { |
181 | | - // an ended live stream |
182 | | - return liveDetails.getString("endTimestamp"); |
183 | | - } else if (!liveDetails.getString("startTimestamp", "").isEmpty()) { |
184 | | - // a running live stream |
185 | | - return liveDetails.getString("startTimestamp"); |
| 194 | + if (!timestamp.isEmpty()) { |
| 195 | + return new DateWrapper(OffsetDateTime.parse(timestamp)); |
186 | 196 | } else if (getStreamType() == StreamType.LIVE_STREAM) { |
187 | 197 | // this should never be reached, but a live stream without upload date is valid |
188 | 198 | return null; |
189 | 199 | } |
190 | 200 |
|
191 | | - final String videoPrimaryInfoRendererDateText = |
192 | | - getTextFromObject(getVideoPrimaryInfoRenderer().getObject("dateText")); |
| 201 | + final var textObject = getVideoPrimaryInfoRenderer().getObject("dateText"); |
| 202 | + return Optional.ofNullable(getTextFromObject(textObject)) |
| 203 | + .flatMap(rendererDateText -> { |
| 204 | + final Optional<LocalDate> dateOptional; |
193 | 205 |
|
194 | | - if (videoPrimaryInfoRendererDateText != null) { |
195 | | - if (videoPrimaryInfoRendererDateText.startsWith("Premiered")) { |
196 | | - final String time = videoPrimaryInfoRendererDateText.substring(13); |
| 206 | + if (rendererDateText.startsWith("Premiered")) { |
| 207 | + final String time = rendererDateText.substring(13); |
197 | 208 |
|
198 | | - try { // Premiered 20 hours ago |
199 | | - final var timeAgoParser = TimeAgoPatternsManager.getTimeAgoParserFor( |
200 | | - new Localization("en")); |
201 | | - final var instant = timeAgoParser.parse(time).getInstant(); |
202 | | - return LocalDate.ofInstant(instant, ZoneId.systemDefault()).toString(); |
203 | | - } catch (final Exception ignored) { |
204 | | - } |
205 | | - |
206 | | - try { // Premiered Feb 21, 2020 |
207 | | - final var formatter = DateTimeFormatter.ofPattern("MMM dd, yyyy", |
208 | | - Locale.ENGLISH); |
209 | | - return LocalDate.parse(time, formatter).toString(); |
210 | | - } catch (final Exception ignored) { |
211 | | - } |
212 | | - |
213 | | - try { // Premiered on 21 Feb 2020 |
214 | | - final var formatter = DateTimeFormatter.ofPattern("dd MMM yyyy", |
215 | | - Locale.ENGLISH); |
216 | | - return LocalDate.parse(time, formatter).toString(); |
217 | | - } catch (final Exception ignored) { |
218 | | - } |
219 | | - } |
| 209 | + try { // Premiered 20 hours ago |
| 210 | + final var localization = new Localization("en"); |
| 211 | + return Optional.of(getTimeAgoParserFor(localization).parse(time)); |
| 212 | + } catch (final Exception e) { |
| 213 | + } |
220 | 214 |
|
221 | | - try { |
222 | | - // TODO: this parses English formatted dates only, we need a better approach to |
223 | | - // parse the textual date |
224 | | - final var formatter = DateTimeFormatter.ofPattern("dd MMM yyyy", Locale.ENGLISH); |
225 | | - return LocalDate.parse(videoPrimaryInfoRendererDateText, formatter).toString(); |
226 | | - } catch (final Exception e) { |
227 | | - throw new ParsingException("Could not get upload date", e); |
228 | | - } |
229 | | - } |
| 215 | + // Premiered Feb 21, 2020 |
| 216 | + dateOptional = parseOptionalDate(time, "MMM dd, yyyy") |
| 217 | + // Premiered on 21 Feb 2020 |
| 218 | + .or(() -> parseOptionalDate(time, "dd MMM yyyy")); |
| 219 | + } else { |
| 220 | + // Premiered on 21 Feb 2020 |
| 221 | + dateOptional = parseOptionalDate(rendererDateText, "dd MMM yyyy"); |
| 222 | + } |
230 | 223 |
|
231 | | - throw new ParsingException("Could not get upload date"); |
| 224 | + return dateOptional.map(date -> { |
| 225 | + final var instant = date.atStartOfDay(ZoneId.systemDefault()).toInstant(); |
| 226 | + return new DateWrapper(instant, true); |
| 227 | + }); |
| 228 | + }) |
| 229 | + .orElseThrow(() -> new ParsingException("Could not get upload date")); |
232 | 230 | } |
233 | 231 |
|
234 | | - @Override |
235 | | - public DateWrapper getUploadDate() throws ParsingException { |
236 | | - final String textualUploadDate = getTextualUploadDate(); |
237 | | - |
238 | | - if (isNullOrEmpty(textualUploadDate)) { |
239 | | - return null; |
| 232 | + private Optional<LocalDate> parseOptionalDate(String date, String pattern) { |
| 233 | + try { |
| 234 | + // TODO: this parses English formatted dates only, we need a better approach to |
| 235 | + // parse the textual date |
| 236 | + final var formatter = DateTimeFormatter.ofPattern(pattern, Locale.ENGLISH); |
| 237 | + return Optional.of(LocalDate.parse(date, formatter)); |
| 238 | + } catch (DateTimeParseException e) { |
| 239 | + return Optional.empty(); |
240 | 240 | } |
241 | | - |
242 | | - return new DateWrapper(YoutubeParsingHelper.parseInstantFrom(textualUploadDate), true); |
243 | 241 | } |
244 | 242 |
|
245 | 243 | @Nonnull |
|
0 commit comments