/* ═══════════════════════════════════════════════════════════════ OUT-OF-CATALOG ARRIVALS (the /new/ surfaces only) OOC titles are auto-ingested and not yet hand-reviewed, so they stay out of fv_build_index() and off every browse page — that index is what /all/, the archives and the homepage rows all read, and every pick there is curator-vouched. "New on" is a different claim: it says a title landed on a service, not that we vouch for it. So the /new/ pages and the homepage's New shelf pull OOC arrivals from here and merge them in, and nothing else sees them. Entries are shaped like fv_build_index() entries so the callers need no special case beyond the merge. Cached for an hour; bump the key to force a rebuild. ═══════════════════════════════════════════════════════════════ */ function agmtw_ooc_arrivals( $geo = 'US' ) { static $memo = []; $map = [ 'US' => 'usa', 'CA' => 'canada', 'GB' => 'uk' ]; if ( ! isset( $map[ $geo ] ) ) $geo = 'US'; if ( isset( $memo[ $geo ] ) ) return $memo[ $geo ]; $key = 'agmtw_ooc_arrivals_v1_' . strtolower( $geo ); $cached = get_transient( $key ); if ( is_array( $cached ) ) return $memo[ $geo ] = $cached; global $wpdb; $prefix = $map[ $geo ]; $cutoff = time() - ( 90 * DAY_IN_SECONDS ); // 1) Arrival stamps first: only OOC titles that actually landed somewhere // recently are worth loading at all. $rows = $wpdb->get_results( $wpdb->prepare( "SELECT m.post_id, m.meta_key, m.meta_value FROM {$wpdb->postmeta} m JOIN {$wpdb->posts} p ON p.ID = m.post_id WHERE p.post_status = 'outofcatalog' AND p.post_type IN ('post','shows') AND m.meta_key LIKE %s AND m.meta_value REGEXP '^[0-9]+$' AND CAST( m.meta_value AS UNSIGNED ) >= %d", $prefix . '-%-date', $cutoff ) ); if ( ! $rows ) { set_transient( $key, [], HOUR_IN_SECONDS ); return $memo[ $geo ] = []; } $plen = strlen( $prefix . '-' ); $slen = strlen( '-date' ); $arrivals = []; foreach ( $rows as $r ) { $streamer = substr( $r->meta_key, $plen, -$slen ); if ( $streamer === '' || $streamer === 'arrival' ) continue; $pid = (int) $r->post_id; $ts = (int) $r->meta_value; if ( ! isset( $arrivals[ $pid ][ $streamer ] ) || $arrivals[ $pid ][ $streamer ] < $ts ) { $arrivals[ $pid ][ $streamer ] = $ts; } } $ids = array_keys( $arrivals ); $ids_csv = implode( ',', array_map( 'intval', $ids ) ); // 2) Titles + type, 3) the taxonomies the /new/ table prints, 4) the // streamer tags an arrival has to be backed by. $meta_rows = $wpdb->get_results( "SELECT ID, post_type, post_title FROM {$wpdb->posts} WHERE ID IN ($ids_csv)" ); $tax_rows = $wpdb->get_results( " SELECT tr.object_id, tt.taxonomy, t.slug FROM {$wpdb->term_relationships} tr JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id JOIN {$wpdb->terms} t ON tt.term_id = t.term_id WHERE tr.object_id IN ($ids_csv) AND tt.taxonomy IN ('category','language','released','post_tag') " ); $sr_rows = $wpdb->get_results( "SELECT post_id, meta_value FROM {$wpdb->postmeta} WHERE post_id IN ($ids_csv) AND meta_key = 'staffrating'" ); $genres = $langs = $years = $streamers = []; foreach ( $tax_rows as $t ) { $pid = (int) $t->object_id; if ( $t->taxonomy === 'category' ) $genres[ $pid ][] = $t->slug; elseif ( $t->taxonomy === 'language' ) $langs[ $pid ][] = $t->slug; elseif ( $t->taxonomy === 'released' ) $years[ $pid ][] = $t->slug; elseif ( strpos( $t->slug, $prefix . '-' ) === 0 ) { $streamers[ $pid ][ substr( $t->slug, $plen ) ] = true; } } $sr = []; foreach ( $sr_rows as $r ) $sr[ (int) $r->post_id ] = (int) $r->meta_value; $out = []; foreach ( $meta_rows as $p ) { $pid = (int) $p->ID; // An arrival with no matching tag means the title has since dropped // off that service; same guard the main index applies. $arr = []; foreach ( ( $arrivals[ $pid ] ?? [] ) as $s => $ts ) { if ( isset( $streamers[ $pid ][ $s ] ) ) $arr[ $s ] = $ts; } if ( ! $arr ) continue; $out[] = [ 'id' => $pid, 'type' => $p->post_type, 'title' => html_entity_decode( $p->post_title, ENT_QUOTES, 'UTF-8' ), 'staffrating' => $sr[ $pid ] ?? 0, 'isPro' => false, 'isOOC' => true, 'streamers' => array_keys( $streamers[ $pid ] ?? [] ), 'genres' => $genres[ $pid ] ?? [], 'languages' => $langs[ $pid ] ?? [], 'released' => $years[ $pid ] ?? [], 'moods' => [], 'countries' => [], 'arrivals' => $arr, 'latestArrival' => max( $arr ), ]; } set_transient( $key, $out, HOUR_IN_SECONDS ); return $memo[ $geo ] = $out; }
The love parents have for their child is powerful, but most parents normally don’t resort to murder for them. Of course, most families don’t actually need to, but like Tetsuo Tosu, they might if their daughter’s yakuza boyfriend plans to murder her first. My Home Hero depicts this scenario, slowly going into the potential sequence of events that could happen, realistically portrayed by Kuranosuke Sasaki, Tae Kimura, and Asuka Saito as the loving family. The family might be a bit cookie-cutter – we’ve seen similar characters in other series before – but they’re relatable enough to make My Home Hero a fairly decent, though generic, crime drama.
A humble office worker's peaceful family life comes to an end after he makes a grave decision for the sake of his family.
Disapproving of his daughter’s abusive boyfriend, normal salaryman Tetsuo Tosu resolves to commit the perfect crime, deciding to murder him. However, this boyfriend was the son of a powerful yakuza head, who’s now out for blood for whoever took him.
My Home Hero has a fairly understandable, if not relatable, premise. After all, many of us would do plenty for our loved ones. While most of us won’t end up resorting to murder, Tosu’s predicament is something that could push us into the deed. That being said, part of the reason he and their family feel relatable is because they don’t have plenty of in-depth characteristics– except, of course, for the ones that make the plot seem plausible, like his expertise in hiding evidence from murder mystery novel writing and his wife’s loyalty from their sordid past. It makes the series relatable, if a bit generic.
Oh, to have a ride-or-die relationship like Tetsuo and Kasen Tosu.