6 * HOUR_IN_SECONDS, 'display' => 'Every 6 hours']; return $s; }); add_action('init', function () { if (!wp_next_scheduled('anj_yt_sync_event')) { wp_schedule_event(time() + 300, 'anj_6h', 'anj_yt_sync_event'); } }); add_action('anj_yt_sync_event', 'anj_yt_sync'); /* ---------- طلب من YouTube API ---------- */ function anj_yt_api($endpoint, $args) { $s = get_option(ANJ_YT_OPT, []); if (empty($s['key'])) throw new Exception('لم يتم إدخال مفتاح API.'); $args['key'] = $s['key']; $r = wp_remote_get('https://www.googleapis.com/youtube/v3/' . $endpoint . '?' . http_build_query($args), ['timeout' => 25]); if (is_wp_error($r)) throw new Exception($r->get_error_message()); $b = json_decode(wp_remote_retrieve_body($r), true); if (!is_array($b)) throw new Exception('رد غير مفهوم من يوتيوب.'); if (isset($b['error'])) throw new Exception('YouTube: ' . ($b['error']['message'] ?? 'خطأ غير معروف')); return $b; } /* كل عناصر قائمة تشغيل (مع تجاوز حد 50 لكل صفحة) */ function anj_yt_playlist_ids($playlist_id) { $ids = []; $token = ''; do { $a = ['part' => 'contentDetails,status', 'playlistId' => $playlist_id, 'maxResults' => 50]; if ($token) $a['pageToken'] = $token; $b = anj_yt_api('playlistItems', $a); foreach ($b['items'] ?? [] as $it) { if (($it['status']['privacyStatus'] ?? '') === 'public') { $ids[] = $it['contentDetails']['videoId']; } } $token = $b['nextPageToken'] ?? ''; } while ($token); return $ids; } /* PT1H2M3S → ثوانٍ */ function anj_yt_seconds($iso) { if (!preg_match('/P(?:(\d+)D)?T?(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?/', (string)$iso, $m)) return 0; return ((int)($m[1] ?? 0)) * 86400 + ((int)($m[2] ?? 0)) * 3600 + ((int)($m[3] ?? 0)) * 60 + (int)($m[4] ?? 0); } /* هل الفيديو Short؟ (رابط /shorts يعطي 200 للـ Shorts ويحوّل للفيديو العادي) */ function anj_yt_is_short($id, $sec) { if ($sec > 180) return 0; $r = wp_remote_head('https://www.youtube.com/shorts/' . $id, ['redirection' => 0, 'timeout' => 10]); if (is_wp_error($r)) return $sec <= 60 ? 1 : 0; return wp_remote_retrieve_response_code($r) == 200 ? 1 : 0; } /* ---------- المزامنة ---------- */ function anj_yt_sync() { @set_time_limit(300); $s = get_option(ANJ_YT_OPT, []); $handle = ltrim(trim($s['handle'] ?? 'Chem31phys'), '@'); try { // 1) القناة وقائمة الرفع $ch = anj_yt_api('channels', ['part' => 'contentDetails', 'forHandle' => $handle]); if (empty($ch['items'][0])) throw new Exception('لم يتم العثور على القناة @' . $handle); $channel_id = $ch['items'][0]['id']; $uploads = $ch['items'][0]['contentDetails']['relatedPlaylists']['uploads']; // 2) كل الفيديوهات العامة $ids = array_values(array_unique(anj_yt_playlist_ids($uploads))); // 3) قوائم التشغيل: فيديو ← أسماء القوائم $pl_map = []; $token = ''; do { $a = ['part' => 'snippet', 'channelId' => $channel_id, 'maxResults' => 50]; if ($token) $a['pageToken'] = $token; $b = anj_yt_api('playlists', $a); foreach ($b['items'] ?? [] as $pl) { $title = $pl['snippet']['title']; foreach (anj_yt_playlist_ids($pl['id']) as $vid) $pl_map[$vid][] = $title; } $token = $b['nextPageToken'] ?? ''; } while ($token); // 4) تفاصيل الفيديوهات (50 في كل طلب) $old = get_option(ANJ_YT_DATA, []); $old_sh = []; foreach ($old['videos'] ?? [] as $v) $old_sh[$v['i']] = $v['sh']; $videos = []; foreach (array_chunk($ids, 50) as $chunk) { $b = anj_yt_api('videos', ['part' => 'snippet,contentDetails,statistics,status', 'id' => implode(',', $chunk)]); foreach ($b['items'] ?? [] as $v) { if (($v['status']['privacyStatus'] ?? '') !== 'public') continue; if (($v['snippet']['liveBroadcastContent'] ?? 'none') !== 'none') continue; $id = $v['id']; $sec = anj_yt_seconds($v['contentDetails']['duration'] ?? ''); $kw = implode(' ', array_slice($v['snippet']['tags'] ?? [], 0, 15)) . ' ' . mb_substr($v['snippet']['description'] ?? '', 0, 200); $videos[] = [ 'i' => $id, 't' => $v['snippet']['title'], 'd' => $sec, 'dt' => substr($v['snippet']['publishedAt'] ?? '', 0, 10), 'v' => (int)($v['statistics']['viewCount'] ?? 0), 'sh' => array_key_exists($id, $old_sh) ? $old_sh[$id] : anj_yt_is_short($id, $sec), 'pl' => $pl_map[$id] ?? [], 'k' => trim($kw), ]; } } if (!$videos) throw new Exception('لم يرجع يوتيوب أي فيديو. لم يتم استبدال البيانات القديمة.'); update_option(ANJ_YT_DATA, ['updated' => time(), 'channel' => '@' . $handle, 'videos' => $videos], false); update_option(ANJ_YT_STATUS, ['ok' => true, 'time' => time(), 'msg' => count($videos) . ' فيديو'], false); return true; } catch (Exception $e) { // عند أي خطأ تبقى آخر بيانات ناجحة كما هي update_option(ANJ_YT_STATUS, ['ok' => false, 'time' => time(), 'msg' => $e->getMessage()], false); return false; } } /* ---------- رابط البيانات للصفحة ---------- */ add_action('rest_api_init', function () { register_rest_route('alnojoum/v1', '/videos', [ 'methods' => 'GET', 'permission_callback' => '__return_true', 'callback' => function () { $d = get_option(ANJ_YT_DATA, ['updated' => 0, 'videos' => []]); $r = rest_ensure_response($d); $r->header('Cache-Control', 'public, max-age=600'); return $r; }, ]); }); /* ---------- صفحة الإعدادات (للمدير فقط) ---------- */ add_action('admin_menu', function () { add_management_page('مكتبة فيديوهات يوتيوب', 'مكتبة يوتيوب', 'manage_options', 'anj-yt', 'anj_yt_admin_page'); }); add_action('admin_post_anj_yt_save', function () { if (!current_user_can('manage_options')) wp_die('غير مسموح'); check_admin_referer('anj_yt_save'); update_option(ANJ_YT_OPT, [ 'key' => sanitize_text_field(wp_unslash($_POST['key'] ?? '')), 'handle' => sanitize_text_field(wp_unslash($_POST['handle'] ?? 'Chem31phys')), ], false); anj_yt_sync(); wp_safe_redirect(admin_url('tools.php?page=anj-yt&done=1')); exit; }); add_action('admin_post_anj_yt_refresh', function () { if (!current_user_can('manage_options')) wp_die('غير مسموح'); check_admin_referer('anj_yt_refresh'); anj_yt_sync(); wp_safe_redirect(admin_url('tools.php?page=anj-yt&done=1')); exit; }); function anj_yt_admin_page() { $s = get_option(ANJ_YT_OPT, ['key' => '', 'handle' => 'Chem31phys']); $st = get_option(ANJ_YT_STATUS, []); $d = get_option(ANJ_YT_DATA, []); $next = wp_next_scheduled('anj_yt_sync_event'); ?>
: —
عدد الفيديوهات في المكتبة الآن: · التحديث التلقائي القادم:
Not a member yet? Register now
Are you a member? Login now