diff --git a/app/[locale]/media-kit/page.tsx b/app/[locale]/media-kit/page.tsx index b5a33b8..8a16c0d 100644 --- a/app/[locale]/media-kit/page.tsx +++ b/app/[locale]/media-kit/page.tsx @@ -2,6 +2,8 @@ import type { Metadata } from "next"; import { notFound } from "next/navigation"; import { MediaKitContent } from "@/components/media-kit-content"; import type { MediaKitLocale } from "@/data/media-kit"; +import { createPageMetadata } from "@/lib/seo"; +import { getYouTubeChannelStats } from "@/lib/youtube-channel-stats"; type MediaKitPageProps = { params: Promise<{ locale: string }>; @@ -13,25 +15,14 @@ export async function generateMetadata({ const { locale } = await params; const isTurkish = locale === "tr"; - return { + return createPageMetadata({ + locale: isTurkish ? "tr" : "en", title: isTurkish ? "Medya Kiti" : "Media Kit", description: isTurkish ? "Poyraz Avsever sponsorluk ve marka iş birlikleri medya kiti." : "Poyraz Avsever media kit for sponsorships and brand partnerships.", - robots: { - index: false, - follow: false, - nocache: true, - googleBot: { - index: false, - follow: false, - noimageindex: true, - }, - }, - alternates: { - canonical: null, - }, - }; + path: "/media-kit", + }); } export default async function MediaKitPage({ params }: MediaKitPageProps) { @@ -41,5 +32,12 @@ export default async function MediaKitPage({ params }: MediaKitPageProps) { notFound(); } - return ; + const youtubeStats = await getYouTubeChannelStats(); + + return ( + + ); } diff --git a/app/robots.ts b/app/robots.ts index 31ff989..6825975 100644 --- a/app/robots.ts +++ b/app/robots.ts @@ -8,7 +8,7 @@ export default function robots(): MetadataRoute.Robots { { userAgent: "*", allow: "/", - disallow: ["/api/", "/_next/", "/media-kit", "/tr/media-kit", "/en/media-kit"], + disallow: ["/api/", "/_next/"], }, ], sitemap: `${SITE_URL}/sitemap.xml`, diff --git a/app/sitemap.ts b/app/sitemap.ts index 60f8090..659940e 100644 --- a/app/sitemap.ts +++ b/app/sitemap.ts @@ -26,6 +26,7 @@ const STATIC_ROUTES = [ { path: "/content", changeFrequency: "weekly", priority: 0.7 }, { path: "/gallery", changeFrequency: "monthly", priority: 0.6 }, { path: "/contact", changeFrequency: "yearly", priority: 0.5 }, + { path: "/media-kit", changeFrequency: "monthly", priority: 0.6 }, { path: "/links", changeFrequency: "monthly", priority: 0.4 }, { path: "/animation-sources", diff --git a/components/media-kit-content.tsx b/components/media-kit-content.tsx index fc6f120..a14b848 100644 --- a/components/media-kit-content.tsx +++ b/components/media-kit-content.tsx @@ -8,158 +8,151 @@ import { ButtonIcon, ButtonLabel, Card, - PatternDots, - Separator, - StatisticCard, - TextEffect, Typography, } from "poyraz-ui/atoms"; import { SPONSORS } from "@/data/sponsors"; import { MEDIA_KIT_AGES, - MEDIA_KIT_CONTENT_BREAKDOWN, + MEDIA_KIT_DEVICES, MEDIA_KIT_GENDER, + MEDIA_KIT_LOCATIONS, MEDIA_KIT_METRICS, MEDIA_KIT_PERIOD, MEDIA_KIT_TOPICS, type MediaKitAudienceRow, + type MediaKitBreakdownRow, type MediaKitLocale, } from "@/data/media-kit"; +import type { YouTubeChannelStats } from "@/lib/youtube-channel-stats"; const COPY = { tr: { - eyebrow: "Medya Kiti · 2026", + eyebrow: "Medya kiti · 2026", availability: "İş birliklerine açık", - name: "Poyraz Avsever", - title: "Teknolojiyi öğreniyor, üretiyor ve anlaşılır şekilde paylaşıyorum.", + title: "Teknolojiyi üretiyor, deneyimliyor ve anlaşılır şekilde paylaşıyorum.", intro: - "En büyük amacım öğrendiklerimi aktarırken bilgimi pekiştirmek ve başkalarının da üretmesine yardımcı olmak. Yazılım, teknoloji ve tasarımı gerçek projeler, dürüst deneyimler ve uygulanabilir anlatımlarla buluşturuyorum.", - role: "İçerik üreticisi · Yazılım geliştirici", - audienceTitle: "Kime ulaşıyorum?", + "Yazılım, teknoloji ve tasarımı gerçek projeler, dürüst deneyimler ve uygulanabilir anlatımlarla buluşturuyorum. Markalarla çalışırken ürünün doğal kullanımını ve izleyici güvenini merkeze alıyorum.", + emailCta: "İletişime geç", + youtubeCta: "YouTube kanalı", + topicsLabel: "İçerik alanları", + statsTitle: "Kanal görünümü", + liveData: "Canlı YouTube verisi", + liveDescription: + "Görüntüleme ve abone toplamı saatlik yenilenir; diğer değerler YouTube Studio raporundan gelir.", + snapshotDescription: + "Ömür boyu performans ve güncel kitle kırılımları YouTube Studio raporundan alınmıştır.", + publicTotal: "Herkese açık kanal toplamı", + audienceTitle: "Kitle profili", audienceDescription: - "Yazılım geliştiriciler, sistem yöneticileri, teknoloji meraklıları ve üretirken öğrenmeyi seven genç profesyoneller.", - statsEyebrow: "YouTube performansı", - statsTitle: "Takipçiden öte, aktif ve büyüyen bir kitle.", - statsDescription: - "Kanal performansının son 28 günlük görünümü. Gelir verileri medya kitine dahil edilmemiştir.", - periodLabel: "Veri dönemi", - audienceEyebrow: "Kitle profili", - audienceHeadline: "İzleyicilerin %68’i 18–34 yaş aralığında.", - audienceBody: - "Teknik kararları etkileyen, yeni araçları deneyen ve teknoloji ürünlerine yüksek ilgi gösteren odaklı bir topluluk.", + "İzleyicilerin %69,1’i 18–34 yaş aralığında ve kitlenin %89,2’si Türkiye’den izliyor.", ageTitle: "Yaş dağılımı", genderTitle: "Cinsiyet dağılımı", - formatTitle: "İçerik formatına göre izlenme", - formatDescription: - "Uzun video odağı, markaların ürün ve hikâyelerini detaylı ve bağlamı korunmuş biçimde anlatmaya alan açıyor.", - reachTitle: "Son 28 günde içerik ritmi", - reachItems: [ - { label: "Yeni izleyici", value: "6,8 B" }, - { label: "Düzenli izleyici", value: "801" }, - { label: "Yayınlanan içerik", value: "9" }, - ], - reachNote: "5 uzun video · 4 Shorts", - partnersEyebrow: "Seçili iş birlikleri", - partnersTitle: "Daha önce birlikte ürettik.", + deviceTitle: "Cihaz türü", + locationsTitle: "En çok izleyen yerler", + activeTitle: "İzleyicilerin aktif olduğu saatler", + activeHours: "16:00–00:00", + activeDescription: + "Hafta boyunca en yoğun zaman aralığı. Hafta sonu hareketlilik öğleden sonra daha erken başlıyor.", + activePeriod: "Yerel saat · GMT +03:00 · Son 28 gün", + male: "Erkek", + female: "Kadın", + partnersTitle: "Birlikte çalıştığım markalar", partnersDescription: - "İçeriğin doğal akışını ve izleyici güvenini koruyan, ürünü gerçekten deneyimlemeye dayalı iş birlikleri.", - collaborationTitle: "Uygun iş birliği formatları", - collaborationItems: [ + "Ürünü gerçekten deneyimlemeye ve içeriğin doğal akışını korumaya dayalı seçili iş birlikleri.", + formatsTitle: "İş birliği formatları", + formats: [ "Sponsorlu YouTube videosu veya doğal ürün entegrasyonu", "Teknik ürün incelemesi ve kullanım senaryosu", - "Kısa format video ve çoklu platform içerik paketi", + "Kısa video ve çoklu platform içerik paketi", "Uzun dönem marka elçiliği ve içerik serisi", ], - ctaEyebrow: "Birlikte üretelim", ctaTitle: "Teknoloji kitlesine güvenilir bir hikâyeyle ulaşın.", ctaDescription: - "Markanızı, ürünü gerçekten anlayan ve izleyicisine açık bir dille anlatan içeriklerle buluşturalım.", - emailCta: "E-posta gönder", - youtubeCta: "YouTube kanalını aç", - footer: "Poyraz Avsever · Medya Kiti", - updated: "Son güncelleme: 1 Temmuz 2026", - derivedNote: - "Ortalama izlenme süresi, paylaşılan toplam izlenme saati ve görüntüleme değerlerinden yaklaşık olarak hesaplanmıştır.", + "Ürününüzü anlayan, deneyimleyen ve izleyicisine açık bir dille anlatan bir iş birliği kurgulayalım.", + dataNote: + "Performans, demografi, cihaz ve ülke kırılımları ömür boyudur. Aktif saatler son 28 günü gösterir.", }, en: { - eyebrow: "Media Kit · 2026", + eyebrow: "Media kit · 2026", availability: "Open to partnerships", - name: "Poyraz Avsever", - title: "I learn, build, and make technology easier to understand.", + title: "I build, experience, and explain technology in a clear way.", intro: - "My biggest goal is to reinforce what I learn by sharing it and help others build along the way. I connect software, technology, and design through real projects, honest experiences, and practical storytelling.", - role: "Content creator · Software developer", - audienceTitle: "Who do I reach?", + "I bring software, technology, and design together through real projects, honest experiences, and practical storytelling. Brand work is grounded in natural product use and audience trust.", + emailCta: "Get in touch", + youtubeCta: "YouTube channel", + topicsLabel: "Content topics", + statsTitle: "Channel overview", + liveData: "Live YouTube data", + liveDescription: + "View and subscriber totals refresh hourly; other figures come from YouTube Studio reports.", + snapshotDescription: + "Lifetime performance and current audience breakdowns come from YouTube Studio reports.", + publicTotal: "Public channel total", + audienceTitle: "Audience profile", audienceDescription: - "Software developers, system administrators, technology enthusiasts, and young professionals who learn by building.", - statsEyebrow: "YouTube performance", - statsTitle: "An active, growing audience beyond follower count.", - statsDescription: - "A 28-day view of channel performance. Revenue data is intentionally excluded from this media kit.", - periodLabel: "Data period", - audienceEyebrow: "Audience profile", - audienceHeadline: "68% of viewers are between 18 and 34.", - audienceBody: - "A focused community that influences technical decisions, tries new tools, and shows strong interest in technology products.", + "69.1% of viewers are between 18 and 34, and 89.2% of the audience watches from Türkiye.", ageTitle: "Age distribution", genderTitle: "Gender distribution", - formatTitle: "Views by content format", - formatDescription: - "A strong long-form focus creates room to explain products and brand stories in depth and with the right context.", - reachTitle: "Content rhythm in the last 28 days", - reachItems: [ - { label: "New viewers", value: "6.8K" }, - { label: "Regular viewers", value: "801" }, - { label: "Published pieces", value: "9" }, - ], - reachNote: "5 long-form videos · 4 Shorts", - partnersEyebrow: "Selected partnerships", - partnersTitle: "Brands I have built with.", + deviceTitle: "Device type", + locationsTitle: "Top geographies", + activeTitle: "When viewers are on YouTube", + activeHours: "4:00 PM–12:00 AM", + activeDescription: + "The busiest window throughout the week. Weekend activity starts earlier in the afternoon.", + activePeriod: "Local time · GMT +03:00 · Last 28 days", + male: "Male", + female: "Female", + partnersTitle: "Brands I have worked with", partnersDescription: - "Partnerships grounded in real product experience while protecting the natural flow of the content and audience trust.", - collaborationTitle: "Partnership formats", - collaborationItems: [ + "Selected partnerships based on genuine product experience and preserving the natural flow of the content.", + formatsTitle: "Partnership formats", + formats: [ "Sponsored YouTube video or native product integration", "Technical product review and use-case content", "Short-form video and multi-platform content package", "Long-term brand ambassadorship and content series", ], - ctaEyebrow: "Let’s build together", ctaTitle: "Reach a technology audience through a credible story.", ctaDescription: - "Let’s introduce your brand through content that understands the product and speaks clearly to its audience.", - emailCta: "Send an email", - youtubeCta: "Open YouTube channel", - footer: "Poyraz Avsever · Media Kit", - updated: "Last updated: July 1, 2026", - derivedNote: - "Average watch time is an estimate calculated from the shared total watch hours and view count.", + "Let’s shape a partnership that understands, experiences, and explains your product clearly.", + dataNote: + "Performance, demographic, device, and geography breakdowns are lifetime figures. Active hours cover the last 28 days.", }, } as const; +function formatCompactNumber(value: number, locale: MediaKitLocale) { + return new Intl.NumberFormat(locale === "tr" ? "tr-TR" : "en-US", { + notation: "compact", + maximumFractionDigits: 1, + }).format(value); +} + function AudienceBar({ item, locale, max = 100, + label, }: { item: MediaKitAudienceRow; locale: MediaKitLocale; max?: number; + label?: string; }) { const width = Math.max((item.value / max) * 100, item.value > 0 ? 2 : 0); - const value = item.value.toLocaleString(locale === "tr" ? "tr-TR" : "en-US", { - maximumFractionDigits: 1, - }); + const formattedValue = item.value.toLocaleString( + locale === "tr" ? "tr-TR" : "en-US", + { maximumFractionDigits: 1 }, + ); return (
- - {item.label} + + {label ?? item.label}
- %{value} + %{formattedValue}
); } -export function MediaKitContent({ locale }: { locale: MediaKitLocale }) { - const copy = COPY[locale]; +function BreakdownBar({ + item, + locale, + max = 100, +}: { + item: MediaKitBreakdownRow; + locale: MediaKitLocale; + max?: number; +}) { + const width = Math.max((item.value / max) * 100, item.value > 0 ? 2 : 0); + const formattedValue = item.value.toLocaleString( + locale === "tr" ? "tr-TR" : "en-US", + { maximumFractionDigits: 1 }, + ); return ( -
-
-
- - Poyraz Avsever -
- - {copy.name} - - - {copy.eyebrow} - -
-
+
+
+ + {item.label[locale]} + + + %{formattedValue} + +
+
+
+
+
+ ); +} +function MetricCard({ + label, + value, + detail, + icon, +}: { + label: string; + value: string; + detail: string; + icon: string; +}) { + return ( + +
+
+ + {label} + + + {value} + +
+ + + +
+ + {detail} + +
+ ); +} + +export function MediaKitContent({ + locale, + youtubeStats, +}: { + locale: MediaKitLocale; + youtubeStats: YouTubeChannelStats | null; +}) { + const copy = COPY[locale]; + const metrics = MEDIA_KIT_METRICS.map((metric) => { + let value = metric.value[locale]; + let detail = metric.detail[locale]; + + if (youtubeStats && metric.id === "views") { + value = formatCompactNumber(youtubeStats.views, locale); + detail = copy.publicTotal; + } + + if (youtubeStats && metric.id === "subscribers") { + value = + youtubeStats.subscribers !== null + ? formatCompactNumber(youtubeStats.subscribers, locale) + : value; + detail = copy.publicTotal; + } + + return { + id: metric.id, + label: metric.label[locale], + value, + detail, + icon: metric.icon, + }; + }); + + return ( +
+
+
+ + {copy.eyebrow} + {copy.availability} -
+
-
-
-
- {MEDIA_KIT_TOPICS.map((topic) => ( - - - {topic.label[locale]} - - ))} +
+
+
+ + {copy.statsTitle} + + + {youtubeStats ? copy.liveDescription : copy.snapshotDescription} + +
+ + {youtubeStats ? ( + + ) : ( + + )} + {youtubeStats ? copy.liveData : MEDIA_KIT_PERIOD[locale]} + +
+ +
+ {metrics.map((metric) => ( + + ))} +
+
+ +
+
+ + {copy.audienceTitle} + + + {copy.audienceDescription} + +
+ +
+ + + {copy.ageTitle} + +
+ {MEDIA_KIT_AGES.map((item) => ( + + ))} +
+
+ + + + {copy.genderTitle} + +
+ {MEDIA_KIT_GENDER.map((item) => ( + + ))} +
+
+ + + + {copy.deviceTitle} + +
+ {MEDIA_KIT_DEVICES.map((item) => ( + + ))} +
+
+ + + + {copy.locationsTitle} + +
+ {MEDIA_KIT_LOCATIONS.map((item) => ( + + ))} +
+
+ + +
+
+ + {copy.activeTitle} + + + {copy.activePeriod} +
+ + +
-
-
+ + {copy.activeHours} + + + {copy.activeDescription} + + +
-
-
-
- - {copy.statsTitle} - - - {copy.statsDescription} - -
-
- - {copy.periodLabel} - - - {MEDIA_KIT_PERIOD[locale]} - -
-
+ + {copy.dataNote} + +
-
- {MEDIA_KIT_METRICS.map((metric) => ( - - - - } - /> +
+
+ + {copy.partnersTitle} + + + {copy.partnersDescription} + +
+ +
+ {SPONSORS.map((sponsor) => { + const card = ( + +
+ {sponsor.name} +
+
+ ); + + return sponsor.websiteUrl ? ( + + {card} + + ) : ( +
{card}
+ ); + })} +
+
+ +
+ + + {copy.formatsTitle} + +
+ {copy.formats.map((item) => ( +
+ + + {item} + +
))}
+
- - * {copy.derivedNote} + + + {copy.ctaTitle} -
- -
-
- - {copy.audienceHeadline} - - - {copy.audienceBody} - - - - - {copy.audienceTitle} - - - {copy.audienceDescription} - -
- {MEDIA_KIT_TOPICS.map((topic) => ( - - {topic.label[locale]} - - ))} -
-
-
- -
- -
- - {copy.genderTitle} - - -
-
- {MEDIA_KIT_GENDER.map((item) => ( - - ))} -
-
- - -
- - {copy.ageTitle} - - -
-
- {MEDIA_KIT_AGES.map((item) => ( - - ))} -
-
-
-
- -
- -
-
- - {copy.formatTitle} - - - {copy.formatDescription} - -
- -
- -
- {MEDIA_KIT_CONTENT_BREAKDOWN.map((item) => ( -
-
- - {item.label[locale]} - -
- - {item.value[locale]} - - - %{item.percentage.toLocaleString( - locale === "tr" ? "tr-TR" : "en-US", - )} - -
-
-
-
-
-
- ))} -
- - - -
- - {copy.reachTitle} - - -
- -
- {copy.reachItems.map((item) => ( -
- - {item.label} - - - {item.value} - -
- ))} -
- - {copy.reachNote} - -
-
- -
-
-
- - {copy.partnersTitle} - - - {copy.partnersDescription} - -
- -
- {SPONSORS.map((sponsor) => ( - - -
- {sponsor.name} -
-
-
- ))} -
-
- - - -
- - {copy.collaborationTitle} - -
- {copy.collaborationItems.map((item) => ( -
- - - - - {item} - -
- ))} -
-
-
- -
- - -
- -
- - {copy.footer} + + {copy.ctaDescription} - - {copy.updated} - -
-
-
+
+ +
+ + + +
+ + {copy.topicsLabel}: {MEDIA_KIT_TOPICS.map((topic) => topic.label[locale]).join(" · ")} + +
+ ); } diff --git a/data/media-kit.ts b/data/media-kit.ts index ef41cd9..3441d06 100644 --- a/data/media-kit.ts +++ b/data/media-kit.ts @@ -14,28 +14,18 @@ export type MediaKitAudienceRow = { value: number; }; +export type MediaKitBreakdownRow = { + id: string; + label: Record; + value: number; +}; + export const MEDIA_KIT_PERIOD = { - tr: "20 Haziran – 17 Temmuz 2026", - en: "June 20 – July 17, 2026", + tr: "Ömür boyu", + en: "Lifetime", } satisfies Record; export const MEDIA_KIT_METRICS: MediaKitMetric[] = [ - { - id: "subscribers", - label: { - tr: "YouTube abonesi", - en: "YouTube subscribers", - }, - value: { - tr: "8 B+", - en: "8K+", - }, - detail: { - tr: "Son 28 günde +715", - en: "+715 in the last 28 days", - }, - icon: "mdi:account-multiple-outline", - }, { id: "views", label: { @@ -43,12 +33,12 @@ export const MEDIA_KIT_METRICS: MediaKitMetric[] = [ en: "Views", }, value: { - tr: "31,1 B", - en: "31.1K", + tr: "788.422", + en: "788,422", }, detail: { - tr: "Son 28 gün", - en: "Last 28 days", + tr: "Ömür boyu", + en: "Lifetime", }, icon: "mdi:play-circle-outline", }, @@ -59,72 +49,97 @@ export const MEDIA_KIT_METRICS: MediaKitMetric[] = [ en: "Watch time", }, value: { - tr: "2,0 B saat", - en: "2.0K hours", + tr: "16,5 B saat", + en: "16.5K hours", }, detail: { - tr: "≈ 3:52 / görüntüleme", - en: "≈ 3:52 per view", + tr: "Ömür boyu", + en: "Lifetime", }, icon: "mdi:clock-outline", }, { - id: "monthly-audience", + id: "subscribers", label: { - tr: "Aylık kitle", - en: "Monthly audience", + tr: "Aboneler", + en: "Subscribers", }, value: { - tr: "15,3 B", - en: "15.3K", + tr: "+9,3 B", + en: "+9.3K", }, detail: { - tr: "Aktif izleyici", - en: "Active viewers", + tr: "Ömür boyu kazanılan", + en: "Gained lifetime", }, - icon: "mdi:chart-line", + icon: "mdi:account-multiple-outline", + }, + { + id: "retention", + label: { + tr: "İzlemeye devam edenler", + en: "Stayed to watch", + }, + value: { + tr: "%59,7", + en: "59.7%", + }, + detail: { + tr: "%40,3 izlemeden geçti", + en: "40.3% swiped away", + }, + icon: "mdi:eye-check-outline", }, ]; -export const MEDIA_KIT_CONTENT_BREAKDOWN = [ +export const MEDIA_KIT_DEVICES: MediaKitBreakdownRow[] = [ { - id: "videos", - label: { - tr: "Uzun videolar", - en: "Long-form videos", - }, - value: { - tr: "24,3 B", - en: "24.3K", - }, - percentage: 78.4, + id: "computer", + label: { tr: "Bilgisayar", en: "Computer" }, + value: 45.2, }, { - id: "shorts", - label: { - tr: "Shorts", - en: "Shorts", - }, - value: { - tr: "6,7 B", - en: "6.7K", - }, - percentage: 21.6, + id: "mobile", + label: { tr: "Cep telefonu", en: "Mobile phone" }, + value: 38.8, }, -] as const; + { id: "tv", label: { tr: "TV", en: "TV" }, value: 11.2 }, + { id: "tablet", label: { tr: "Tablet", en: "Tablet" }, value: 4.7 }, +]; + +export const MEDIA_KIT_LOCATIONS: MediaKitBreakdownRow[] = [ + { id: "turkey", label: { tr: "Türkiye", en: "Türkiye" }, value: 89.2 }, + { + id: "azerbaijan", + label: { tr: "Azerbaycan", en: "Azerbaijan" }, + value: 3, + }, + { id: "germany", label: { tr: "Almanya", en: "Germany" }, value: 1.1 }, + { + id: "united-states", + label: { tr: "Amerika Birleşik Devletleri", en: "United States" }, + value: 0.2, + }, + { + id: "netherlands", + label: { tr: "Hollanda", en: "Netherlands" }, + value: 0.2, + }, +]; export const MEDIA_KIT_GENDER: MediaKitAudienceRow[] = [ - { id: "male", label: "Erkek", value: 98 }, - { id: "female", label: "Kadın", value: 2 }, + { id: "female", label: "Kadın", value: 5.7 }, + { id: "male", label: "Erkek", value: 94.3 }, ]; export const MEDIA_KIT_AGES: MediaKitAudienceRow[] = [ - { id: "18-24", label: "18–24", value: 21.9 }, - { id: "25-34", label: "25–34", value: 46.1 }, - { id: "35-44", label: "35–44", value: 20.8 }, - { id: "45-54", label: "45–54", value: 9.8 }, - { id: "55-64", label: "55–64", value: 1.2 }, - { id: "65+", label: "65+", value: 0.1 }, + { id: "13-17", label: "13–17", value: 1.5 }, + { id: "18-24", label: "18–24", value: 23.7 }, + { id: "25-34", label: "25–34", value: 45.4 }, + { id: "35-44", label: "35–44", value: 19.1 }, + { id: "45-54", label: "45–54", value: 8.5 }, + { id: "55-64", label: "55–64", value: 1.5 }, + { id: "65+", label: "65+", value: 0.3 }, ]; export const MEDIA_KIT_TOPICS = [ diff --git a/data/sponsors.ts b/data/sponsors.ts index 2a3f372..1a51726 100644 --- a/data/sponsors.ts +++ b/data/sponsors.ts @@ -12,28 +12,30 @@ export const SPONSORS: Sponsor[] = [ name: "Hostinger", job: "Web Hosting", logo: "/sponsors/hostinger.png", - websiteUrl: "https://hostinger.com", + websiteUrl: "https://hostinger.com/poyraz", }, { id: "testsprite", name: "TestSprite", job: "Yazılım Test Otomasyonu", logo: "/sponsors/testsprite.png", - websiteUrl: "https://testsprite.com", + websiteUrl: "https://www.testsprite.com/?via=poyraz", }, { id: "minimax", name: "MiniMax", job: "AI Video & Model Platformu", logo: "/sponsors/minimax.png", - websiteUrl: "https://www.minimax.io", + websiteUrl: + "https://platform.minimax.io/subscribe/coding-plan?code=7aH9b0Ya7c&source=link", }, { id: "higgsfield", name: "Higgsfield", job: "AI Creative Suite", logo: "/sponsors/higgsfield.png", - websiteUrl: "https://higgsfield.ai", + websiteUrl: + "https://higgsfield.ai/s/higgsfield-mcp-3-0-yt-poyrazavsever-lLvqMw", }, { id: "hosting-dunyam", diff --git a/lib/youtube-channel-stats.ts b/lib/youtube-channel-stats.ts new file mode 100644 index 0000000..fc4be32 --- /dev/null +++ b/lib/youtube-channel-stats.ts @@ -0,0 +1,65 @@ +export type YouTubeChannelStats = { + subscribers: number | null; + views: number; + videos: number; +}; + +type YouTubeChannelsResponse = { + items?: Array<{ + statistics?: { + hiddenSubscriberCount?: boolean; + subscriberCount?: string; + viewCount?: string; + videoCount?: string; + }; + }>; +}; + +function parseCount(value: string | undefined) { + if (!value) return null; + const parsed = Number.parseInt(value, 10); + return Number.isFinite(parsed) ? parsed : null; +} + +export async function getYouTubeChannelStats(): Promise { + const apiKey = process.env.YOUTUBE_API_KEY?.trim(); + if (!apiKey) return null; + + const params = new URLSearchParams({ + part: "statistics", + key: apiKey, + }); + const channelId = process.env.YOUTUBE_CHANNEL_ID?.trim(); + + if (channelId) { + params.set("id", channelId); + } else { + params.set("forHandle", "@poyrazavsever"); + } + + try { + const response = await fetch( + `https://www.googleapis.com/youtube/v3/channels?${params.toString()}`, + { next: { revalidate: 3600 } }, + ); + + if (!response.ok) return null; + + const data = (await response.json()) as YouTubeChannelsResponse; + const statistics = data.items?.[0]?.statistics; + const views = parseCount(statistics?.viewCount); + const videos = parseCount(statistics?.videoCount); + + if (!statistics || views === null || videos === null) return null; + + return { + subscribers: statistics.hiddenSubscriberCount + ? null + : parseCount(statistics.subscriberCount), + views, + videos, + }; + } catch { + return null; + } +}