Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
76d2e8f
feat(AniTilky): modern rewrite for anitilky.com
hyqanxd Jul 14, 2026
0a5e50e
fix(AniTilky): pass pmd validation and update assets
hyqanxd Jul 14, 2026
d5b7b0b
fix(AniTilky): resolve covers blocked by BunnyCDN
hyqanxd Jul 14, 2026
f5904c3
fix(AniTilky): fetch cover blobs with referer
hyqanxd Jul 14, 2026
ae8c3e1
fix(AniTilky): blob covers only, drop broken small icons
hyqanxd Jul 14, 2026
9d297bf
fix(AniTilky): unstick watch presence and restore covers/buttons
hyqanxd Jul 14, 2026
115bfcd
fix(AniTilky): blob covers via fetch and reliable pause detection
hyqanxd Jul 14, 2026
df10eb9
fix(AniTilky): keep animated GIF profile avatars
hyqanxd Jul 14, 2026
1d33446
revert(AniTilky): drop broken blob pipeline, restore stable covers
hyqanxd Jul 14, 2026
bdd77bc
fix(AniTilky): fetch BunnyCDN images as blobs including GIFs
hyqanxd Jul 14, 2026
ff45ea6
fix(AniTilky): convert large GIFs to JPEG with reliable logo fallback
hyqanxd Jul 14, 2026
52e0975
fix(AniTilky): resolve CI lint and set version to 2.0.0
hyqanxd Jul 14, 2026
8fa4e60
feat(AniTilky): switch to new brand logo
hyqanxd Jul 14, 2026
d7de6a6
feat(AniTilky): use imgur logo for CDN asset migration
hyqanxd Jul 14, 2026
8c0a327
feat(AniTilky): update logo asset
hyqanxd Jul 14, 2026
f98b794
Merge branch 'main' into update/anitilky-v2
hyqanxd Jul 16, 2026
f0cb030
Merge branch 'main' into update/anitilky-v2
hyqanxd Jul 16, 2026
b64f313
Update websites/A/AniTilky/metadata.json
hyqanxd Jul 16, 2026
94bac12
Merge branch 'main' into update/anitilky-v2
hyqanxd Jul 16, 2026
3281f13
feat(AniTilky): use landscape thumbnail
hyqanxd Jul 16, 2026
c5ceb32
Merge branch 'main' into update/anitilky-v2
hyqanxd Jul 17, 2026
a1fe1b5
Merge branch 'main' into update/anitilky-v2
hyqanxd Jul 20, 2026
b610889
Merge branch 'main' into update/anitilky-v2
hyqanxd Jul 21, 2026
51ba7d6
Merge branch 'main' into update/anitilky-v2
hyqanxd Jul 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions websites/A/AniTilky/functions/fetchAnime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { Anime } from '../types.js'
import { API_URL, fetchCached } from './helpers.js'

export async function fetchAnime(id: string): Promise<Anime | null> {
return fetchCached(`anime:${id}`, async () => {
try {
const response = await fetch(`${API_URL}/anime/${encodeURIComponent(id)}`)
if (!response.ok)
return null
return await response.json() as Anime
}
catch {
return null
}
})
}
20 changes: 20 additions & 0 deletions websites/A/AniTilky/functions/fetchUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { UserProfile } from '../types.js'
import { API_URL, ActivityAssets, fetchCached } from './helpers.js'

Check failure

Code scanning / ESLint

Enforce sorted named imports. Error

Expected "ActivityAssets" to come before "API_URL".
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

export async function fetchUser(username: string): Promise<UserProfile | null> {
return fetchCached(`user:${username.toLowerCase()}`, async () => {
try {
const response = await fetch(`${API_URL}/user/profile/${encodeURIComponent(username)}`)
if (!response.ok)
return null
const data = await response.json() as UserProfile
return {
...data,
profileImage: data.profileImage || ActivityAssets.Logo,
}
}
catch {
return null
}
})
}
86 changes: 86 additions & 0 deletions websites/A/AniTilky/functions/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import type { Anime, AnimeEpisode, AnimeSeason } from '../types.js'

export const BASE_URL = 'https://anitilky.com'
export const API_URL = 'https://api.anitilky.com/api'

export enum ActivityAssets {
Logo = 'https://cdn.rcd.gg/PreMiD/websites/A/AniTilky/assets/logo.png',
Thumbnail = 'https://cdn.rcd.gg/PreMiD/websites/A/AniTilky/assets/thumbnail.png',

Check failure

Code scanning / PMD

Makes sure all images (logo and URLs) are exactly 512x512 pixels Error

Image URL dimensions must be exactly 512x512 pixels, got 1919x955 for URL: https://cdn.rcd.gg/PreMiD/websites/A/AniTilky/assets/thumbnail.png
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
}

const cache = new Map<string, { data: unknown, expires: number }>()
const CACHE_TTL = 60_000

export async function fetchCached<T>(key: string, fetcher: () => Promise<T | null>): Promise<T | null> {
const cached = cache.get(key)
if (cached && cached.expires > Date.now())
return cached.data as T

const data = await fetcher()
if (data)
cache.set(key, { data, expires: Date.now() + CACHE_TTL })

return data
}

export function getAnimeTitle(anime?: Anime | null): string | undefined {
if (!anime?.title)
return undefined
return anime.title.romaji || anime.title.english || anime.title.native
}

export function findSeason(anime: Anime | null | undefined, seasonNumber: number): AnimeSeason | undefined {
return anime?.seasons?.find(s => s.seasonNumber === seasonNumber)
}

export function findEpisode(season: AnimeSeason | undefined, episodeNumber: number): AnimeEpisode | undefined {
return season?.episodes?.find(e => e.episodeNumber === episodeNumber)
}

export function getOgImage(): string | undefined {
return document.querySelector<HTMLMetaElement>('meta[property="og:image"]')?.content
|| document.querySelector<HTMLMetaElement>('meta[name="twitter:image"]')?.content
}

export function getPageTitle(): string {
return document.title.replace(/\s*\|\s*ANITILKY.*$/i, '').trim()
}

export function resolveCoverImage(preferred?: string, fallback?: string): string {
return preferred || fallback || getOgImage() || ActivityAssets.Logo
}

export function getStoredUsername(): string | undefined {
try {
const raw = localStorage.getItem('userData') || sessionStorage.getItem('userData')
if (!raw)
return undefined
const parsed = JSON.parse(raw) as { username?: string }
return parsed.username
}
catch {
return undefined
}
}

export function getProfileUsernameFromDom(): string | undefined {
return document.querySelector<HTMLElement>('main h4, [class*="MuiTypography-h4"]')?.textContent?.trim()
|| getStoredUsername()
}

export function parsePathSegments(pathname: string): string[] {
return pathname.split('/').filter(Boolean)
}

export function formatEpisodeState(
template: string,
season: number,
episode: number,
episodeTitle?: string,
): string {
const base = template
.replace('{0}', String(season))
.replace('{1}', String(episode))

return episodeTitle ? `${base} — ${episodeTitle}` : base
}
118 changes: 118 additions & 0 deletions websites/A/AniTilky/functions/strings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import type { Presence } from 'premid'

Check failure

Code scanning / PMD

Makes sure the TypeScript files are correct Error

Module '"premid"' has no exported member 'Presence'.
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

export interface PresenceStrings {
play: string
pause: string
browse: string
search: string
viewPage: string
viewSeries: string
watchEpisode: string
viewProfile: string
watchingSeries: string
seasonEpisode: string
home: string
animeList: string
schedule: string
users: string
notifications: string
settings: string
donation: string
login: string
register: string
admin: string
viewingAnime: string
viewingProfile: string
ownProfile: string
tabPosts: string
tabMedia: string
tabWatchlist: string
tabFavorites: string
tabHistory: string
privacyWatching: string
privacyBrowsing: string
}

const SITE_STRINGS: Record<'en' | 'tr', Omit<PresenceStrings,
'play' | 'pause' | 'browse' | 'search' | 'viewPage' | 'viewSeries' | 'watchEpisode' | 'watchingSeries'

Check failure

Code scanning / ESLint

Having line breaks styles to object, array and named imports Error

Should not have line breaks between items, in node TSTypeParameterInstantiation

Check failure

Code scanning / ESLint

Having line breaks styles to object, array and named imports Error

Should not have line breaks between items, in node TSTypeParameterInstantiation
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
>> = {
en: {
viewProfile: 'View profile',
seasonEpisode: 'Season {0} · Episode {1}',
home: 'Home',
animeList: 'Anime catalog',
schedule: 'Release schedule',
users: 'Community',
notifications: 'Notifications',
settings: 'Account settings',
donation: 'Support AniTilky',
login: 'Signing in',
register: 'Creating account',
admin: 'Admin panel',
viewingAnime: 'Viewing anime',
viewingProfile: 'Viewing profile',
ownProfile: 'My profile',
tabPosts: 'Posts',
tabMedia: 'Media',
tabWatchlist: 'Watchlist',
tabFavorites: 'Favorites',
tabHistory: 'Watch history',
privacyWatching: 'Watching content',
privacyBrowsing: 'Browsing AniTilky',
},
tr: {
viewProfile: 'Profile git',
seasonEpisode: 'Sezon {0} · Bölüm {1}',
home: 'Ana sayfa',
animeList: 'Anime kataloğu',
schedule: 'Yayın takvimi',
users: 'Topluluk',
notifications: 'Bildirimler',
settings: 'Hesap ayarları',
donation: 'AniTilky\'ye destek',
login: 'Giriş yapılıyor',
register: 'Hesap oluşturuluyor',
admin: 'Yönetim paneli',
viewingAnime: 'Anime inceleniyor',
viewingProfile: 'Profil görüntüleniyor',
ownProfile: 'Kendi profili',
tabPosts: 'Gönderiler',
tabMedia: 'Medya',
tabWatchlist: 'İzleme listesi',
tabFavorites: 'Favoriler',
tabHistory: 'İzleme geçmişi',
privacyWatching: 'İçerik izleniyor',
privacyBrowsing: 'AniTilky\'de geziniyor',
},
}

export async function getStrings(presence: Presence, lang: string): Promise<PresenceStrings> {
const normalized = lang.startsWith('tr') ? 'tr' : 'en'
const general = await presence.getStrings(
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
{
play: 'general.playing',
pause: 'general.paused',
browse: 'general.browsing',
search: 'general.search',
viewPage: 'general.viewPage',
viewSeries: 'general.buttonViewSeries',
watchEpisode: 'general.buttonViewEpisode',
watchingSeries: 'general.watchingSeries',
},
lang,
)

return { ...general, ...SITE_STRINGS[normalized] }
}

export type ProfileTabKey = keyof Pick<PresenceStrings,
'tabPosts' | 'tabMedia' | 'tabWatchlist' | 'tabFavorites' | 'tabHistory'

Check failure

Code scanning / ESLint

Having line breaks styles to object, array and named imports Error

Should not have line breaks between items, in node TSTypeParameterInstantiation

Check failure

Code scanning / ESLint

Having line breaks styles to object, array and named imports Error

Should not have line breaks between items, in node TSTypeParameterInstantiation
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
>

export const PROFILE_TABS: Record<string, ProfileTabKey> = {
posts: 'tabPosts',
media: 'tabMedia',
watchlist: 'tabWatchlist',
favorites: 'tabFavorites',
history: 'tabHistory',
}
89 changes: 81 additions & 8 deletions websites/A/AniTilky/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,98 @@
"$schema": "https://schemas.premid.app/metadata/1.16",
Comment thread
hyqanxd marked this conversation as resolved.
Outdated
"apiVersion": 1,
"author": {
"name": "hakanxd",
"id": "725520758117498881"
"name": "hakanxd2",
"id": "726522818522775564"
},
"service": "AniTilky",
"description": {
"en": "Watch Turkish dubbed and subtitled anime, track your progress, and discover new series.",
"tr": "Türkçe dublajlı ve altyazılı anime izle, ilerlemenizi takip edin ve yeni seriler keşfedin."
"de": "Schau türkisch synchronisierte und untertitelte Anime, verfolge deinen Fortschritt und entdecke neue Serien auf AniTilky.",
"en": "Watch Turkish dubbed and subtitled anime, track your progress, and explore the community on AniTilky.",
"fr": "Regardez des anime doublés et sous-titrés en turc, suivez votre progression et découvrez de nouvelles séries sur AniTilky.",
"nl": "Bekijk Turks nagesynchroniseerde en ondertitelde anime, volg je voortgang en ontdek nieuwe series op AniTilky.",
"pt-br": "Assista anime dublado e legendado em turco, acompanhe seu progresso e descubra novas séries no AniTilky.",
"ro": "Urmărește anime dublat și subtitrat în turcă, urmărește progresul tău și descoperă serii noi pe AniTilky.",
"tr": "Türkçe dublajlı ve altyazılı anime izle, ilerlemenizi takip edin ve yeni seriler keşfedin.",
"uk": "Дивіться аніме з турецьким дубляжем і субтитрами, відстежуйте прогрес і відкривайте нові серіали на AniTilky.",
"vi": "Xem anime lồng tiếng và phụ đề tiếng Thổ Nhĩ Kỳ, theo dõi tiến trình và khám phá cộng đồng trên AniTilky."
},
"url": "anitilky.xyz",
"regExp": "^https?[:][/][/]anitilky[.]xyz[/]",
"version": "1.1.1",
"url": [
"anitilky.com",
"www.anitilky.com"
],
"regExp": "^https?[:][/][/](www[.])?anitilky[.]com[/]",
"version": "2.0.0",
"logo": "https://cdn.rcd.gg/PreMiD/websites/A/AniTilky/assets/logo.png",
"thumbnail": "https://cdn.rcd.gg/PreMiD/websites/A/AniTilky/assets/thumbnail.png",
"color": "#FF6B6B",
"color": "#ff0000",
"category": "anime",
"tags": [
"anime",
"video",
"streaming",
"turkish"
],
"settings": [
{
"id": "lang",
"multiLanguage": true
},
{
"id": "privacy",
"title": "Privacy Mode",
"icon": "fad fa-user-secret",
"value": false
},
{
"id": "showBrowsingStatus",
"title": "Show Browsing Status",
"icon": "fad fa-book-reader",
"value": true
},
{
"id": "showCover",
"title": "Show Cover Art",
"icon": "fas fa-images",
"value": true,
"if": {
"privacy": false
}
},
{
"id": "showTimestamp",
"title": "Show Timestamps",
"icon": "fad fa-stopwatch",
"value": true,
"if": {
"privacy": false
}
},
{
"id": "showSmallImages",
"title": "Show Status Icons",
"icon": "fad fa-pause",
"value": true,
"if": {
"privacy": false
}
},
{
"id": "showButtons",
"title": "Show Buttons",
"icon": "fas fa-external-link-alt",
"value": true,
"if": {
"privacy": false
}
},
{
"id": "showProfiles",
"title": "Show Profile Activity",
"icon": "fad fa-user-circle",
"value": true,
"if": {
"privacy": false
}
}
]
}
Loading
Loading