/**
* AniList Episode & Season Importer for AnimeStream
*/
add_action('add_meta_boxes', 'ts_anilist_importer_add_metabox');
function ts_anilist_importer_add_metabox() {
add_meta_box(
'ts-anilist-importer',
'AniList Full Importer',
'ts_anilist_importer_callback',
'anime',
'normal',
'high'
);
}
function ts_anilist_importer_callback($post) {
$mal_id = get_post_meta($post->ID, 'ero_series_generate', true);
?>
Fetch all data (Series info, Seasons, and Episodes) directly from AniList.
}
add_action('wp_ajax_ts_fetch_anilist_full', 'ts_handle_fetch_anilist_full');
function ts_handle_fetch_anilist_full() {
$initial_id = intval($_POST['id']);
$post_id = intval($_POST['post_id']);
if (!$initial_id) wp_send_json_error('Invalid ID');
$media = ts_get_anilist_media_details($initial_id);
if (!$media) wp_send_json_error('Anime not found');
$franchise_ids = [$initial_id];
$queue = [$initial_id];
$depth = 0;
$discovered_nodes = [$initial_id => $media];
while (!empty($queue) && $depth < 12) {
$curr_id = array_shift($queue);
$curr_node = $discovered_nodes[$curr_id] ?? ts_get_anilist_media_details($curr_id);
if ($curr_node && !empty($curr_node['relations']['edges'])) {
foreach ($curr_node['relations']['edges'] as $edge) {
$node = $edge['node'];
if (in_array($edge['relationType'], ['SEQUEL', 'PREQUEL', 'PARENT']) && !in_array($node['idMal'], $franchise_ids)) {
if ($node['type'] === 'ANIME' && !empty($node['idMal'])) {
$franchise_ids[] = $node['idMal'];
$queue[] = $node['idMal'];
$depth++;
}
}
}
}
}
$franchise = [];
foreach ($franchise_ids as $fid) {
$node = ($fid == $initial_id) ? $media : ts_get_anilist_media_details($fid);
if ($node && in_array($node['format'], ['TV', 'TV_SHORT', 'ONA', 'MOVIE'])) {
$f_title = !empty($node['title']['romaji']) ? $node['title']['romaji'] : $node['title']['romaji'];
$franchise[] = [
'title' => $f_title,
'idMal' => $node['idMal'],
'year' => $node['startDate']['year'] ?? '?',
'status' => $node['status'],
'sort_key' => sprintf('%04d%02d%02d', $node['startDate']['year'] ?? 0, $node['startDate']['month'] ?? 0, $node['startDate']['day'] ?? 0)
];
}
}
$episodes = [];
$existing_nums = [];
$existing_posts = get_posts(['post_type'=>'post', 'meta_key'=>'ero_seri', 'meta_value'=>$post_id, 'posts_per_page'=>-1]);
foreach ($existing_posts as $p) {
$n = get_post_meta($p->ID, 'ero_episodebaru', true);
if ($n) $existing_nums[] = (int)$n;
}
$manual_count = isset($_POST['manual_ep_count']) ? intval($_POST['manual_ep_count']) : 0;
$total = (int)$media['episodes'];
$max_stream = 0;
$next_airing = 0;
if (!empty($media['streamingEpisodes'])) {
foreach ($media['streamingEpisodes'] as $s) {
if (preg_match('/Episode (\d+)\b/i', $s['title'], $m)) {
$max_stream = max($max_stream, (int)$m[1]);
}
}
}
if (isset($media['nextAiringEpisode']['episode'])) {
$next_airing = $media['nextAiringEpisode']['episode'] - 1;
}
if ($manual_count > 0) {
$loop = $manual_count;
} else {
$loop = max($total, $max_stream, $next_airing);
}
if ($loop <= 0 && $media['status'] == 'RELEASING') $loop = 1;
for ($i = 1; $i <= $loop; $i++) {
$etitle = "Episode $i";
if (!empty($media['streamingEpisodes'])) {
foreach ($media['streamingEpisodes'] as $s) {
if (preg_match('/Episode ' . $i . '\b/i', $s['title'])) {
$etitle = $s['title'];
break;
}
}
}
$episodes[] = ['num' => $i, 'title' => $etitle, 'exists' => in_array($i, $existing_nums)];
}
$status_map = ['FINISHED'=>'Completed', 'RELEASING'=>'Ongoing', 'NOT_YET_RELEASED'=>'Upcoming', 'HIATUS'=>'Hiatus'];
usort($franchise, function($a, $b) {
return strcmp($a['sort_key'], $b['sort_key']);
});
$main_title = !empty($media['title']['romaji']) ? $media['title']['romaji'] : $media['title']['romaji'];
wp_send_json_success([
'title' => $main_title,
'description' => strip_tags($media['description']),
'cover' => $media['coverImage']['large'],
'banner' => $media['bannerImage'] ?? '',
'type' => $media['format'],
'status_mapped' => $status_map[$media['status']] ?? 'Ongoing',
'status_label' => $media['status'],
'year' => $media['seasonYear'],
'season' => ucfirst(strtolower($media['season'])),
'score' => $media['averageScore'] / 10,
'genres_str' => implode(', ', $media['genres']),
'studios_str' => implode(', ', array_column($media['studios']['nodes'], 'name')),
'total_ep_count' => $loop,
'episodes' => $episodes,
'relations' => $franchise,
'debug' => [
'manual_override' => $manual_count,
'episodes_field' => $total,
'max_from_streaming' => $max_stream,
'next_airing_episode' => $next_airing,
'final_count' => $loop
]
]);
}
add_action('wp_ajax_ts_create_series_from_rel', 'ts_handle_create_series_from_rel');
function ts_handle_create_series_from_rel() {
$mal_id = intval($_POST['id']);
if (!$mal_id) wp_send_json_error('Invalid MAL ID');
$exists = get_posts(['post_type'=>'anime', 'meta_key'=>'ero_series_generate', 'meta_value'=>$mal_id, 'posts_per_page'=>1]);
if ($exists) wp_send_json_error('This series already exists in your database.');
$data = ts_get_anilist_media_details($mal_id);
$main_title = ($data && !empty($data['title']['english'])) ? $data['title']['english'] : ($data ? $data['title']['romaji'] : "New Series ($mal_id)");
$desc = $data ? $data['description'] : "";
$new_id = wp_insert_post([
'post_title' => $main_title,
'post_content' => $desc,
'post_type' => 'anime',
'post_status' => 'publish'
]);
if ($new_id) {
update_post_meta($new_id, 'ero_series_generate', $mal_id);
ts_set_yoast_seo_meta($new_id, $main_title, $desc, 'series');
wp_send_json_success(['edit_url' => get_edit_post_link($new_id, 'url')]);
}
wp_send_json_error('Failed to create series post');
}
function ts_get_anilist_media_details($id) {
if (!$id) return null;
$media = ts_perform_anilist_query($id, 'id');
if (!$media) {
$media = ts_perform_anilist_query($id, 'idMal');
}
return $media;
}
function ts_perform_anilist_query($id, $field = 'id') {
$query = '
query ($id: Int) {
Media (' . $field . ': $id, type: ANIME) {
id idMal title { romaji english }
description episodes status averageScore format season seasonYear
nextAiringEpisode { episode }
coverImage { large }
bannerImage
genres
startDate { year month day }
studios(isMain: true) { nodes { name } }
streamingEpisodes { title url }
relations { edges { relationType node { idMal title { romaji english } type format } } }
}
}';
$response = wp_remote_post('https://graphql.anilist.co', [
'body' => json_encode(['query' => $query, 'variables' => ["id"=>$id]]),
'headers' => ['Content-Type' => 'application/json']
]);
if (is_wp_error($response)) return null;
$body = json_decode(wp_remote_retrieve_body($response), true);
return $body['data']['Media'] ?? null;
}
add_action('wp_ajax_ts_process_episode_v2', 'ts_handle_process_episode_v2');
function ts_handle_process_episode_v2() {
$ep = $_POST['episode'];
$series_id = intval($_POST['series_id']);
$ep_num = intval($ep['num']);
$prev = get_posts(['post_type'=>'post', 'posts_per_page'=>1, 'meta_query'=>[['key'=>'ero_seri','value'=>$series_id],['key'=>'ero_episodebaru','value'=>$ep_num]]]);
if ($prev) wp_send_json_success('Skip');
$series_title = get_the_title($series_id);
$ep_title_clean = !empty($ep['title']) ? $ep['title'] : "Episode $ep_num";
// Series title se URL slug banao
$series_slug = strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $series_title)));
// Trailing dash hatao
$series_slug = trim($series_slug, '-');
$episode_post_title = $series_title . ' Episode ' . $ep_num . ' English Subbed ';
// Unique Episode Content for SEO
$post_content = "Dear Anime Lovers, {$series_title} Episode {$ep_num} English Subbed is released. You can now watch {$series_title} latest ep with Eng sub. ";
$post_content .= "Aniwatchtv is best site which provides {$series_title} Ep {$ep_num} Eng Sub.";
$new_id = wp_insert_post([
'post_title' => $episode_post_title,
'post_content' => $post_content,
'post_type' => 'post',
'post_status' => 'publish',
'post_author' => get_current_user_id()
]);
if ($new_id) {
update_post_meta($new_id, 'ero_seri', $series_id);
update_post_meta($new_id, 'ero_episodebaru', $ep_num);
update_post_meta($new_id, 'ero_episodetitle', $ep_title_clean);
update_post_meta($new_id, 'ero_subepisode', 'Sub');
$series_thumbnail_id = get_post_thumbnail_id($series_id);
if ($series_thumbnail_id) {
set_post_thumbnail($new_id, $series_thumbnail_id);
}
$cats = wp_get_post_categories($series_id);
if ($cats) {
wp_set_post_categories($new_id, $cats);
}
$genres = wp_get_post_terms($series_id, 'genres', ['fields' => 'ids']);
if (!is_wp_error($genres) && !empty($genres)) {
wp_set_post_terms($new_id, $genres, 'genres');
}
$studios = wp_get_post_terms($series_id, 'studio', ['fields' => 'ids']);
if (!is_wp_error($studios) && !empty($studios)) {
wp_set_post_terms($new_id, $studios, 'studio');
}
$seasons = wp_get_post_terms($series_id, 'season', ['fields' => 'ids']);
if (!is_wp_error($seasons) && !empty($seasons)) {
wp_set_post_terms($new_id, $seasons, 'season');
}
wp_send_json_success('Created');
}
wp_send_json_error('Fail');
}
add_action('wp_ajax_ts_import_seo_full', 'ts_handle_import_seo_full');
function ts_handle_import_seo_full() {
$url = esc_url_raw($_POST['image_url']);
$banner_url = isset($_POST['banner_url']) ? esc_url_raw($_POST['banner_url']) : '';
$post_id = intval($_POST['post_id']);
$title = sanitize_text_field($_POST['title']);
$desc = sanitize_textarea_field($_POST['description']);
if (!$post_id) wp_send_json_error('Missing ID');
require_once(ABSPATH . 'wp-admin/includes/media.php');
require_once(ABSPATH . 'wp-admin/includes/file.php');
require_once(ABSPATH . 'wp-admin/includes/image.php');
if (!has_post_thumbnail($post_id) && $url) {
$seo_name = sanitize_title($title) . '-poster';
$tmp = download_url($url);
if (!is_wp_error($tmp)) {
$file_array = ['name' => $seo_name . '.jpg', 'tmp_name' => $tmp];
$att_id = media_handle_sideload($file_array, $post_id, $title);
if (!is_wp_error($att_id)) {
update_post_meta($att_id, '_wp_attachment_image_alt', $title . ' Poster');
wp_update_post(['ID' => $att_id, 'post_excerpt' => 'Poster for ' . $title]);
set_post_thumbnail($post_id, $att_id);
}
}
}
if ($banner_url) {
$banner_name = sanitize_title($title) . '-banner';
$tmp_banner = download_url($banner_url);
if (!is_wp_error($tmp_banner)) {
$file_array = ['name' => $banner_name . '.jpg', 'tmp_name' => $tmp_banner];
$banner_att_id = media_handle_sideload($file_array, $post_id, $title . ' Banner');
if (!is_wp_error($banner_att_id)) {
update_post_meta($banner_att_id, '_wp_attachment_image_alt', $title . ' Banner');
wp_update_post(['ID' => $banner_att_id, 'post_excerpt' => 'Banner for ' . $title]);
update_post_meta($post_id, 'ero_cover', $banner_att_id);
update_post_meta($post_id, 'ero_slider', '1');
}
}
}
ts_set_yoast_seo_meta($post_id, $title, $desc, 'series');
wp_send_json_success('SEO Updated');
}
function ts_set_yoast_seo_meta($post_id, $title, $description, $type = 'series', $series_title = '', $ep_num = '') {
$site_name = get_bloginfo('name');
if ($type === 'series') {
$seo_title = $title . " Online - Watch " . $title . " Full Episodes";
$seo_desc = "Watch " . $title . " online for free with English sub and dub. " . wp_trim_words($description, 25);
$focus_kw = $title;
} else {
$seo_title = $series_title . " Episode " . $ep_num . " Online - " . $site_name;
$seo_desc = "Watch " . $series_title . " Episode " . $ep_num . " online for free. Full episode title: " . $title . ". Find more episodes of " . $series_title . " here.";
$focus_kw = $series_title . " Episode " . $ep_num;
}
update_post_meta($post_id, '_yoast_wpseo_title', $seo_title);
update_post_meta($post_id, '_yoast_wpseo_metadesc', $seo_desc);
update_post_meta($post_id, '_yoast_wpseo_focuskw', $focus_kw);
}
Hoshino Makoto Archives - AniWatch