PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.1-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.1-beta
16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / jetpack_vendor / automattic / jetpack-podcast / src / feed / class-feed-detection.php
jetpack / jetpack_vendor / automattic / jetpack-podcast / src / feed Last commit date
class-customize-feed.php 1 month ago class-episode-block-tags.php 1 month ago class-feed-detection.php 2 months ago
class-feed-detection.php
109 lines
1 <?php
2 /**
3 * Records when podcast directory crawlers fetch the feed.
4 *
5 * @package automattic/jetpack-podcast
6 */
7
8 declare( strict_types = 1 );
9
10 namespace Automattic\Jetpack\Podcast\Feed;
11
12 use Automattic\Jetpack\Podcast\Settings;
13
14 /**
15 * Promotes an existing pending podcatcher `podcasting_show_states` entry to
16 * `'active'` the first time we see its UA fetch the feed. Idempotent
17 * thereafter.
18 */
19 class Feed_Detection {
20
21 /**
22 * UA needle → podcatcher slug. First match wins, so order matters: more
23 * specific brand needles (e.g. `GooglePodcasts`, `YouTubeMusic`) come
24 * before generic ones (e.g. Apple's `Podcasts/`) that they share a
25 * substring with.
26 *
27 * Cross-referenced against opawg/user-agents-v2 (the OPAWG community
28 * dataset used by Podtrac, Megaphone, Art19, Chartable). When directories
29 * change UAs or new ones emerge, sync the relevant entries here:
30 * https://github.com/opawg/user-agents-v2/blob/master/src/{apps,bots}.json
31 *
32 * @var array<string, string>
33 */
34 private const NEEDLES = array(
35 // YouTube / Google Podcasts — listed first so `GooglePodcasts/`
36 // doesn't get caught by Apple's broader `Podcasts/` needle below.
37 'Google-Podcast' => 'youtube',
38 'YouTube-Podcast' => 'youtube',
39 'GooglePodcasts' => 'youtube',
40 'GoogleChirp' => 'youtube',
41 'YouTubeMusic' => 'youtube',
42
43 // Apple Podcasts (iOS app, Mac app, automated checks, HomePod, Apple TV).
44 'iTMS' => 'apple',
45 'AppleCoreMedia' => 'apple',
46 'Podcasts/' => 'apple',
47 'iTunes' => 'apple',
48 'AirPodcasts/' => 'apple',
49
50 // Spotify — substring catches `Spotify/…`, `spotify-rss-…`, all variants.
51 'Spotify' => 'spotify',
52
53 // Pocket Casts.
54 'Pocket Casts' => 'pocketcasts',
55 'PocketCasts' => 'pocketcasts',
56
57 // Amazon — `AmazonMusic` is the listening app; `Amazon Music Podcast`
58 // (with spaces) is the actual feed crawler. Keep both.
59 'AmazonMusic' => 'amazon',
60 'Amazon Music Podcast' => 'amazon',
61
62 // Podcast Index — substring `PodcastIndex` catches `Podcastindex.org/`,
63 // `PodcastIndexer/`, `PodcastIndexManager/`, `PodcastIndex Classifier/`.
64 'PodcastIndex' => 'podcastindex',
65 );
66
67 /**
68 * Inspect the current request's User-Agent and, if it's a directory
69 * crawler with an existing pending state, mark its state `'active'`.
70 * No-op if the UA is missing, not in the directory allowlist, or the
71 * matched directory has no pending state.
72 */
73 public static function detect_and_record(): void {
74 $ua = isset( $_SERVER['HTTP_USER_AGENT'] )
75 ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) )
76 : '';
77 if ( '' === $ua ) {
78 return;
79 }
80
81 $slug = null;
82 foreach ( self::NEEDLES as $needle => $candidate ) {
83 if ( false !== stripos( $ua, $needle ) ) {
84 $slug = $candidate;
85 break;
86 }
87 }
88
89 if ( null === $slug || ! isset( Settings::SHOW_URL_HOSTS[ $slug ] ) ) {
90 return;
91 }
92
93 $states = get_option( 'podcasting_show_states', array() );
94 if ( ! is_array( $states ) ) {
95 $states = array();
96 }
97
98 if ( ! isset( $states[ $slug ] ) || 'pending' !== $states[ $slug ] ) {
99 return;
100 }
101
102 // Concurrent first-fetches from different apps can race the read-modify-write.
103 // Benign: the loser's next poll (multi-hour cadence) re-writes its key. Worst
104 // case is one app sitting in `pending` for one extra poll cycle.
105 $states[ $slug ] = 'active';
106 update_option( 'podcasting_show_states', $states );
107 }
108 }
109