|
1 | 1 | package org.schabi.newpipe.extractor.services.youtube; |
2 | 2 |
|
3 | | -import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.defaultAlertsCheck; |
4 | | -import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getJsonPostResponse; |
5 | | -import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject; |
6 | | -import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.hasArtistOrVerifiedIconBadgeAttachment; |
7 | | -import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.prepareDesktopJsonBuilder; |
8 | | -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; |
9 | | - |
10 | 3 | import com.grack.nanojson.JsonObject; |
11 | 4 | import com.grack.nanojson.JsonWriter; |
12 | | - |
13 | 5 | import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; |
14 | 6 | import org.schabi.newpipe.extractor.exceptions.ExtractionException; |
15 | 7 | import org.schabi.newpipe.extractor.exceptions.ParsingException; |
16 | 8 | import org.schabi.newpipe.extractor.localization.ContentCountry; |
17 | 9 | import org.schabi.newpipe.extractor.localization.Localization; |
18 | 10 |
|
| 11 | +import javax.annotation.Nonnull; |
| 12 | +import javax.annotation.Nullable; |
19 | 13 | import java.io.IOException; |
20 | 14 | import java.io.Serializable; |
21 | 15 | import java.nio.charset.StandardCharsets; |
| 16 | +import java.util.Objects; |
22 | 17 | import java.util.Optional; |
23 | 18 |
|
24 | | -import javax.annotation.Nonnull; |
25 | | -import javax.annotation.Nullable; |
| 19 | +import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.STRING_PREDICATE; |
| 20 | +import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.defaultAlertsCheck; |
| 21 | +import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getJsonPostResponse; |
| 22 | +import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject; |
| 23 | +import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.hasArtistOrVerifiedIconBadgeAttachment; |
| 24 | +import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.prepareDesktopJsonBuilder; |
| 25 | +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; |
26 | 26 |
|
27 | 27 | /** |
28 | 28 | * Shared functions for extracting YouTube channel pages and tabs. |
@@ -444,100 +444,88 @@ public static String getChannelId( |
444 | 444 | @Nullable final ChannelHeader channelHeader, |
445 | 445 | @Nonnull final JsonObject jsonResponse, |
446 | 446 | @Nullable final String fallbackChannelId) throws ParsingException { |
447 | | - if (channelHeader != null) { |
448 | | - switch (channelHeader.headerType) { |
449 | | - case C4_TABBED: |
| 447 | + final var headerType = channelHeader != null ? channelHeader.headerType : null; |
| 448 | + final Optional<String> channelIdOptional; |
| 449 | + if (headerType == null) { |
| 450 | + channelIdOptional = Optional.empty(); |
| 451 | + } else { |
| 452 | + switch (headerType) { |
| 453 | + case C4_TABBED: { |
450 | 454 | final String channelId = channelHeader.json.getObject(HEADER) |
451 | 455 | .getObject(C4_TABBED_HEADER_RENDERER) |
452 | | - .getString("channelId", ""); |
453 | | - if (!isNullOrEmpty(channelId)) { |
454 | | - return channelId; |
455 | | - } |
456 | | - final String navigationC4TabChannelId = channelHeader.json |
457 | | - .getObject("navigationEndpoint") |
458 | | - .getObject(BROWSE_ENDPOINT) |
459 | | - .getString(BROWSE_ID); |
460 | | - if (!isNullOrEmpty(navigationC4TabChannelId)) { |
461 | | - return navigationC4TabChannelId; |
462 | | - } |
| 456 | + .getString("channelId"); |
| 457 | + channelIdOptional = Optional.ofNullable(channelId) |
| 458 | + .or(() -> Optional.ofNullable(getBrowseId(channelHeader.json))); |
463 | 459 | break; |
464 | | - case CAROUSEL: |
465 | | - final String navigationCarouselChannelId = channelHeader.json.getObject(HEADER) |
| 460 | + } |
| 461 | + case CAROUSEL: { |
| 462 | + channelIdOptional = channelHeader.json.getObject(HEADER) |
466 | 463 | .getObject(CAROUSEL_HEADER_RENDERER) |
467 | 464 | .getArray(CONTENTS) |
468 | | - .stream() |
469 | | - .filter(JsonObject.class::isInstance) |
470 | | - .map(JsonObject.class::cast) |
471 | | - .filter(item -> item.has(TOPIC_CHANNEL_DETAILS_RENDERER)) |
| 465 | + .streamAsJsonObjects() |
| 466 | + .map(item -> item.getObject(TOPIC_CHANNEL_DETAILS_RENDERER, null)) |
| 467 | + .filter(Objects::nonNull) |
472 | 468 | .findFirst() |
473 | | - .orElse(new JsonObject()) |
474 | | - .getObject(TOPIC_CHANNEL_DETAILS_RENDERER) |
475 | | - .getObject("navigationEndpoint") |
476 | | - .getObject(BROWSE_ENDPOINT) |
477 | | - .getString(BROWSE_ID); |
478 | | - if (!isNullOrEmpty(navigationCarouselChannelId)) { |
479 | | - return navigationCarouselChannelId; |
480 | | - } |
| 469 | + .map(YoutubeChannelHelper::getBrowseId); |
481 | 470 | break; |
| 471 | + } |
482 | 472 | default: |
483 | | - break; |
| 473 | + channelIdOptional = Optional.empty(); |
484 | 474 | } |
485 | 475 | } |
486 | 476 |
|
487 | | - final String externalChannelId = jsonResponse.getObject("metadata") |
488 | | - .getObject("channelMetadataRenderer") |
489 | | - .getString("externalChannelId"); |
490 | | - if (!isNullOrEmpty(externalChannelId)) { |
491 | | - return externalChannelId; |
492 | | - } |
| 477 | + return channelIdOptional |
| 478 | + .or(() -> { |
| 479 | + final String externalChannelId = jsonResponse.getObject("metadata") |
| 480 | + .getObject("channelMetadataRenderer") |
| 481 | + .getString("externalChannelId"); |
| 482 | + return Optional.ofNullable(externalChannelId); |
| 483 | + }) |
| 484 | + .or(() -> Optional.ofNullable(fallbackChannelId)) |
| 485 | + .filter(STRING_PREDICATE) |
| 486 | + .orElseThrow(() -> new ParsingException("Could not get channel ID")); |
| 487 | + } |
493 | 488 |
|
494 | | - if (!isNullOrEmpty(fallbackChannelId)) { |
495 | | - return fallbackChannelId; |
496 | | - } else { |
497 | | - throw new ParsingException("Could not get channel ID"); |
498 | | - } |
| 489 | + @Nullable |
| 490 | + private static String getBrowseId(@Nonnull final JsonObject jsonObject) { |
| 491 | + return jsonObject.getObject("navigationEndpoint") |
| 492 | + .getObject(BROWSE_ENDPOINT) |
| 493 | + .getString(BROWSE_ID); |
499 | 494 | } |
500 | 495 |
|
501 | 496 | @Nonnull |
502 | 497 | public static String getChannelName(@Nullable final ChannelHeader channelHeader, |
503 | 498 | @Nullable final JsonObject channelAgeGateRenderer, |
504 | 499 | @Nonnull final JsonObject jsonResponse) |
505 | 500 | throws ParsingException { |
506 | | - if (channelAgeGateRenderer != null) { |
507 | | - final String title = channelAgeGateRenderer.getString("channelTitle"); |
508 | | - if (isNullOrEmpty(title)) { |
509 | | - throw new ParsingException("Could not get channel name"); |
510 | | - } |
511 | | - return title; |
512 | | - } |
513 | | - |
514 | | - final String metadataRendererTitle = jsonResponse.getObject("metadata") |
515 | | - .getObject("channelMetadataRenderer") |
516 | | - .getString(TITLE); |
517 | | - if (!isNullOrEmpty(metadataRendererTitle)) { |
518 | | - return metadataRendererTitle; |
519 | | - } |
520 | | - |
521 | | - return Optional.ofNullable(channelHeader) |
522 | | - .flatMap(header -> { |
523 | | - final JsonObject channelJson = header.json; |
524 | | - switch (header.headerType) { |
525 | | - case PAGE: |
526 | | - final String pageTitle = channelJson.getObject(CONTENT) |
527 | | - .getObject(PAGE_HEADER_VIEW_MODEL) |
528 | | - .getObject(TITLE) |
529 | | - .getObject("dynamicTextViewModel") |
530 | | - .getObject("text") |
531 | | - .getString(CONTENT, channelJson.getString("pageTitle")); |
532 | | - return Optional.ofNullable(pageTitle); |
533 | | - case CAROUSEL: |
534 | | - case INTERACTIVE_TABBED: |
535 | | - return getTextFromObject(channelJson.getObject(TITLE)); |
536 | | - case C4_TABBED: |
537 | | - default: |
538 | | - return Optional.ofNullable(channelJson.getString(TITLE)); |
539 | | - } |
540 | | - }) |
| 501 | + final String channelTitle = channelAgeGateRenderer != null |
| 502 | + ? channelAgeGateRenderer.getString("channelTitle") : null; |
| 503 | + return Optional.ofNullable(channelTitle) |
| 504 | + .or(() -> Optional.ofNullable(jsonResponse.getObject("metadata") |
| 505 | + .getObject("channelMetadataRenderer") |
| 506 | + .getString(TITLE))) |
| 507 | + .filter(STRING_PREDICATE) |
| 508 | + .or(() -> Optional.ofNullable(channelHeader) |
| 509 | + .flatMap(header -> { |
| 510 | + final JsonObject channelJson = header.json; |
| 511 | + switch (header.headerType) { |
| 512 | + case PAGE: |
| 513 | + final String pageTitle = channelJson.getObject(CONTENT) |
| 514 | + .getObject(PAGE_HEADER_VIEW_MODEL) |
| 515 | + .getObject(TITLE) |
| 516 | + .getObject("dynamicTextViewModel") |
| 517 | + .getObject("text") |
| 518 | + .getString(CONTENT, channelJson.getString("pageTitle")); |
| 519 | + return Optional.ofNullable(pageTitle); |
| 520 | + case CAROUSEL: |
| 521 | + case INTERACTIVE_TABBED: |
| 522 | + return getTextFromObject(channelJson.getObject(TITLE)); |
| 523 | + case C4_TABBED: |
| 524 | + default: |
| 525 | + return Optional.ofNullable(channelJson.getString(TITLE)); |
| 526 | + } |
| 527 | + }) |
| 528 | + ) |
541 | 529 | // The channel name from a microformatDataRenderer may be different from the one |
542 | 530 | // displayed, especially for auto-generated channels, depending on the language |
543 | 531 | // requested for the interface (hl parameter of InnerTube requests' payload) |
|
0 commit comments