X-Git-Url: https://git.cweiske.de/stouyapi.git/blobdiff_plain/1adc46e8fa60e7e7dff2dc4ca57376371adf79ab..3771acf28944bb6d7e82bc425e7745c5524ae199:/bin/import-game-data.php diff --git a/bin/import-game-data.php b/bin/import-game-data.php index 3d6545f..854b3d6 100755 --- a/bin/import-game-data.php +++ b/bin/import-game-data.php @@ -7,22 +7,54 @@ * @author Christian Weiske */ ini_set('xdebug.halt_level', E_WARNING|E_NOTICE|E_USER_WARNING|E_USER_NOTICE); +require_once __DIR__ . '/functions.php'; require_once __DIR__ . '/filters.php'; -if (!isset($argv[1])) { + +//command line option parsing +$optind = null; +$opts = getopt('h', ['help', 'mini', 'noqr'], $optind); +$args = array_slice($argv, $optind); + +if (isset($opts['help']) || isset($opts['h'])) { + echo "Import games from a OUYA game data repository\n"; + echo "\n"; + echo "Usage: import-game-data.php [--mini] [--noqr] [--help|-h]\n"; + echo " --mini Generate small but ugly JSON files\n"; + echo " --noqr Do not generate and link QR code images\n"; + exit(0); +} + +if (!isset($args[0])) { error('Pass the path to a "folders" file with game data json files folder names'); } -$foldersFile = $argv[1]; +$foldersFile = $args[0]; if (!is_file($foldersFile)) { error('Given path is not a file: ' . $foldersFile); } -$GLOBALS['packagelists']['cweiskepicks'] = [ - 'de.eiswuxe.blookid2', - 'com.cosmos.babyloniantwins' -]; +$cfgMini = isset($opts['mini']); +$cfgEnableQr = !isset($opts['noqr']); + + +//default configuration values +$GLOBALS['baseUrl'] = 'http://ouya.cweiske.de/'; +$GLOBALS['categorySubtitles'] = []; +$GLOBALS['packagelists'] = []; +$GLOBALS['urlRewrites'] = []; +$cfgFile = __DIR__ . '/../config.php'; +if (file_exists($cfgFile)) { + include $cfgFile; +} $wwwDir = __DIR__ . '/../www/'; +if ($cfgEnableQr) { + $qrDir = $wwwDir . 'gen-qr/'; + if (!is_dir($qrDir)) { + mkdir($qrDir, 0775); + } +} + $baseDir = dirname($foldersFile); $gameFiles = []; foreach (file($foldersFile) as $line) { @@ -39,8 +71,18 @@ foreach (file($foldersFile) as $line) { } } +//store git repository version of last folder +$workdir = getcwd(); +chdir($folder); +$gitDate = `git log --max-count=1 --format="%h %cI"`; +chdir($workdir); +file_put_contents($wwwDir . '/game-data-version', $gitDate); + $games = []; $count = 0; +$developers = []; + +//load game data. doing early to collect a developer's games foreach ($gameFiles as $gameFile) { $game = json_decode(file_get_contents($gameFile)); if ($game === null) { @@ -49,17 +91,41 @@ foreach ($gameFiles as $gameFile) { addMissingGameProperties($game); $games[$game->packageName] = $game; + if (!isset($developers[$game->developer->uuid])) { + $developers[$game->developer->uuid] = [ + 'info' => $game->developer, + 'products' => [], + 'gameNames' => [], + ]; + } + $developers[$game->developer->uuid]['gameNames'][] = $game->packageName; +} + +//write json api files +foreach ($games as $game) { + $products = $game->products ?? []; + foreach ($products as $product) { + writeJson( + 'api/v1/developers/' . $game->developer->uuid + . '/products/' . $product->identifier . '.json', + buildDeveloperProductOnly($product, $game->developer) + ); + $developers[$game->developer->uuid]['products'][] = $product; + } + writeJson( 'api/v1/details-data/' . $game->packageName . '.json', - buildDetails($game) + buildDetails( + $game, + count($developers[$game->developer->uuid]['gameNames']) > 1 + ) ); - /* this crashes babylonian twins + writeJson( 'api/v1/games/' . $game->packageName . '/purchases', - "{}\n" + buildPurchases($game) ); - */ - + writeJson( 'api/v1/apps/' . $game->packageName . '.json', buildApps($game) @@ -80,12 +146,56 @@ foreach ($gameFiles as $gameFile) { } } -writeJson('api/v1/discover-data/discover.json', buildDiscover($games)); +calculateRank($games); + +foreach ($developers as $developer) { + writeJson( + //index.htm does not need a rewrite rule + 'api/v1/developers/' . $developer['info']->uuid + . '/products/index.htm', + buildDeveloperProducts($developer['products'], $developer['info']) + ); + writeJson( + 'api/v1/developers/' . $developer['info']->uuid + . '/current_gamer', + buildDeveloperCurrentGamer() + ); + + if (count($developer['gameNames']) > 1) { + writeJson( + 'api/v1/discover-data/dev--' . $developer['info']->uuid . '.json', + buildSpecialCategory( + 'Developer: ' . $developer['info']->name, + filterByPackageNames($games, $developer['gameNames']) + ) + ); + } +} + +$data = buildDiscover($games); +writeJson('api/v1/discover-data/discover.json', $data); +writeJson('api/v1/discover-data/discover.forge.json', convertCategoryToForge($data)); writeJson('api/v1/discover-data/home.json', buildDiscoverHome($games)); +//make +writeJson( + 'api/v1/discover-data/tutorials.json', + buildMakeCategory('Tutorials', filterByGenre($games, 'Tutorials')) +); + +$searchLetters = 'abcdefghijklmnopqrstuvwxyz0123456789., '; +foreach (str_split($searchLetters) as $letter) { + $letterGames = filterBySearchWord($games, $letter); + writeJson( + 'api/v1/search-data/' . $letter . '.json', + buildSearch($letterGames) + ); +} + function buildDiscover(array $games) { + $games = removeMakeGames($games); $data = [ 'title' => 'DISCOVER', 'rows' => [], @@ -93,29 +203,78 @@ function buildDiscover(array $games) ]; addDiscoverRow( - $data, 'Last Updated', - filterLastUpdated($games, 10) + $data, 'New games', + filterLastAdded($games, 10) ); addDiscoverRow( - $data, 'Best rated', - filterBestRated($games, 10) + $data, 'Best rated games', + filterBestRatedGames($games, 10), + true ); + + foreach ($GLOBALS['packagelists'] as $listTitle => $listPackageNames) { + addDiscoverRow( + $data, $listTitle, + filterByPackageNames($games, $listPackageNames) + ); + } + addDiscoverRow( - $data, "cweiske's picks", - filterByPackageNames($games, $GLOBALS['packagelists']['cweiskepicks']) + $data, 'Special', + [ + 'Best rated', + 'Best rated games', + 'Most rated', + 'Random', + 'Last updated', + ] + ); + writeCategoryJson( + 'api/v1/discover-data/' . categoryPath('Best rated') . '.json', + buildSpecialCategory('Best rated', filterBestRated($games, 99)) + ); + writeCategoryJson( + 'api/v1/discover-data/' . categoryPath('Best rated games') . '.json', + buildSpecialCategory('Best rated games', filterBestRatedGames($games, 99)) ); - + writeCategoryJson( + 'api/v1/discover-data/' . categoryPath('Most rated') . '.json', + buildSpecialCategory('Most rated', filterMostDownloaded($games, 99)) + ); + writeCategoryJson( + 'api/v1/discover-data/' . categoryPath('Random') . '.json', + buildSpecialCategory( + 'Random ' . date('Y-m-d H:i'), + filterRandom($games, 99) + ) + ); + writeCategoryJson( + 'api/v1/discover-data/' . categoryPath('Last updated') . '.json', + buildSpecialCategory('Last updated', filterLastUpdated($games, 99)) + ); + $players = [ //1 => '1 player', 2 => '2 players', 3 => '3 players', 4 => '4 players', ]; - addDiscoverRow($data, '# of players', $players); + addDiscoverRow($data, 'Multiplayer', $players); foreach ($players as $num => $title) { - writeJson( + writeCategoryJson( 'api/v1/discover-data/' . categoryPath($title) . '.json', - buildDiscoverCategory($title, filterByPlayers($games, $num)) + buildDiscoverCategory( + $title, + //I do not want emulators here, + // and neither Streaming apps + filterByGenre( + filterByGenre( + filterByPlayers($games, $num), + 'Emulator', true + ), + 'App', true + ) + ) ); } @@ -123,18 +282,18 @@ function buildDiscover(array $games) natsort($ages); addDiscoverRow($data, 'Content rating', $ages); foreach ($ages as $num => $title) { - writeJson( + writeCategoryJson( 'api/v1/discover-data/' . categoryPath($title) . '.json', buildDiscoverCategory($title, filterByAge($games, $title)) ); } - $genres = getAllGenres($games); + $genres = removeMakeGenres(getAllGenres($games)); sort($genres); addChunkedDiscoverRows($data, $genres, 'Genres'); foreach ($genres as $genre) { - writeJson( + writeCategoryJson( 'api/v1/discover-data/' . categoryPath($genre) . '.json', buildDiscoverCategory($genre, filterByGenre($games, $genre)) ); @@ -143,7 +302,7 @@ function buildDiscover(array $games) $abc = array_merge(range('A', 'Z'), ['Other']); addChunkedDiscoverRows($data, $abc, 'Alphabetical'); foreach ($abc as $letter) { - writeJson( + writeCategoryJson( 'api/v1/discover-data/' . categoryPath($letter) . '.json', buildDiscoverCategory($letter, filterByLetter($games, $letter)) ); @@ -162,22 +321,103 @@ function buildDiscoverCategory($name, $games) 'rows' => [], 'tiles' => [], ]; - addDiscoverRow( - $data, 'Last Updated', - filterLastUpdated($games, 10) - ); - addDiscoverRow( - $data, 'Best rated', - filterBestRated($games, 10) - ); + if (isset($GLOBALS['categorySubtitles'][$name])) { + $data['stouyapi']['subtitle'] = $GLOBALS['categorySubtitles'][$name]; + } - usort( - $games, - function ($gameA, $gameB) { - return strcmp($gameA->title, $gameB->title); - } - ); + if (count($games) >= 20) { + addDiscoverRow( + $data, 'Last updated', + filterLastUpdated($games, 10) + ); + addDiscoverRow( + $data, 'Best rated', + filterBestRated($games, 10), + true + ); + } + + $games = sortByTitle($games); $chunks = array_chunk($games, 4); + $title = 'All'; + foreach ($chunks as $chunkGames) { + addDiscoverRow($data, $title, $chunkGames); + $title = ''; + } + + return $data; +} + +/** + * Modify a category to make it suitable for the Razer Forge TV + * + * - Fold rows without title into the previous row + * - Remove automatically generated categories ("Last updated", "Best rated") + * + * @see buildDiscoverCategory() + */ +function convertCategoryToForge($data, $removeAutoCategories = false) +{ + //merge tiles from rows without title into the previous row + $lastTitleRowId = null; + foreach ($data['rows'] as $rowId => $row) { + if ($row['title'] !== '') { + $lastTitleRowId = $rowId; + } else if ($lastTitleRowId !== null) { + $data['rows'][$lastTitleRowId]['tiles'] = array_merge( + $data['rows'][$lastTitleRowId]['tiles'], + $row['tiles'] + ); + unset($data['rows'][$rowId]); + } + } + + if ($removeAutoCategories) { + foreach ($data['rows'] as $rowId => $row) { + if ($row['title'] === 'Last updated' + || $row['title'] === 'Best rated' + ) { + unset($data['rows'][$rowId]); + } + } + } + + $data['rows'] = array_values($data['rows']); + + return $data; +} + +function buildMakeCategory($name, $games) +{ + $data = [ + 'title' => $name, + 'rows' => [], + 'tiles' => [], + ]; + + $games = sortByTitle($games); + addDiscoverRow($data, '', $games); + + return $data; +} + +/** + * Category without the "Last updated" or "Best rated" top rows + * + * Used for "Best rated", "Most rated", "Random" + */ +function buildSpecialCategory($name, $games) +{ + $data = [ + 'title' => $name, + 'rows' => [], + 'tiles' => [], + ]; + + $first3 = array_slice($games, 0, 3); + $chunks = array_chunk(array_slice($games, 3), 4); + array_unshift($chunks, $first3); + foreach ($chunks as $chunkGames) { addDiscoverRow($data, '', $chunkGames); } @@ -187,19 +427,29 @@ function buildDiscoverCategory($name, $games) function buildDiscoverHome(array $games) { - //we do not want anything here for now $data = [ 'title' => 'home', 'rows' => [ - [ - 'title' => 'FEATURED', - 'showPrice' => false, - 'ranked' => false, - 'tiles' => [], - ] ], 'tiles' => [], ]; + + if (isset($GLOBALS['home'])) { + reset($GLOBALS['home']); + $title = key($GLOBALS['home']); + addDiscoverRow( + $data, $title, + filterByPackageNames($games, $GLOBALS['home'][$title]) + ); + } else { + $data['rows'][] = [ + 'title' => 'FEATURED', + 'showPrice' => false, + 'ranked' => false, + 'tiles' => [], + ]; + } + return $data; } @@ -210,6 +460,12 @@ function buildApps($game) { $latestRelease = $game->latestRelease; + $product = null; + $gamePromoted = getPromotedProduct($game); + if ($gamePromoted) { + $product = buildProduct($gamePromoted); + } + // http://cweiske.de/ouya-store-api-docs.htm#get-https-devs-ouya-tv-api-v1-apps-xxx return [ 'app' => [ @@ -237,9 +493,9 @@ function buildApps($game) 'publicSize' => $latestRelease->publicSize, 'nativeSize' => $latestRelease->nativeSize, - 'mainImageFullUrl' => $game->media->discover, - 'videoUrl' => $game->media->video, - 'filepickerScreenshots' => $game->media->screenshots, + 'mainImageFullUrl' => $game->discover, + 'videoUrl' => getFirstVideoUrl($game->media), + 'filepickerScreenshots' => getAllImageUrls($game->media), 'mobileAppIcon' => null, 'developer' => $game->developer->name, @@ -247,7 +503,7 @@ function buildApps($game) 'supportPhone' => $game->developer->supportPhone, 'founder' => $game->developer->founder, - 'promotedProduct' => null, + 'promotedProduct' => $product, ], ]; } @@ -264,46 +520,94 @@ function buildAppDownload($game, $release) ]; } +function buildProduct($product) +{ + if ($product === null) { + return null; + } + return [ + 'type' => $product->type ?? 'entitlement', + 'identifier' => $product->identifier, + 'name' => $product->name, + 'description' => $product->description ?? '', + 'localPrice' => $product->localPrice, + 'originalPrice' => $product->originalPrice, + 'priceInCents' => $product->originalPrice * 100, + 'percentOff' => 0, + 'currency' => $product->currency, + ]; +} + /** * Build /app/v1/details?app=org.example.game */ -function buildDetails($game) +function buildDetails($game, $linkDeveloperPage = false) { $latestRelease = $game->latestRelease; $mediaTiles = []; - if ($game->media->discover) { + if ($game->discover) { $mediaTiles[] = [ 'type' => 'image', 'urls' => [ - 'thumbnail' => $game->media->discover, - 'full' => $game->media->discover, + 'thumbnail' => $game->discover, + 'full' => $game->discover, ], - 'fp_url' => $game->media->discover, ]; } - if ($game->media->video) { - $mediaTiles[] = [ - 'type' => 'video', - 'url' => $game->media->video, - ]; + foreach ($game->media as $medium) { + if ($medium->type == 'image') { + $mediaTiles[] = [ + 'type' => 'image', + 'urls' => [ + 'thumbnail' => $medium->thumb ?? $medium->url, + 'full' => $medium->url, + ], + ]; + } else { + if (!isUnsupportedVideoUrl($medium->url)) { + $mediaTiles[] = [ + 'type' => 'video', + 'url' => $medium->url, + ]; + } + } } - foreach ($game->media->screenshots as $screenshot) { - $mediaTiles[] = [ - 'type' => 'image', - 'urls' => [ - 'thumbnail' => $screenshot, - 'full' => $screenshot, - ], - 'fp_url' => $screenshot, + + $buttons = []; + if (isset($game->links->unlocked)) { + $buttons[] = [ + 'text' => 'Show unlocked', + 'url' => 'ouya://launcher/details?app=' . $game->links->unlocked, + 'bold' => true, ]; } + $product = null; + $gamePromoted = getPromotedProduct($game); + if ($gamePromoted) { + $product = buildProduct($gamePromoted); + } + + $iaUrl = null; + if (isset($game->latestRelease->url) + && substr($game->latestRelease->url, 0, 29) == 'https://archive.org/download/' + ) { + $iaUrl = dirname($game->latestRelease->url) . '/'; + } + + $description = $game->description; + if (isset($game->notes) && trim($game->notes)) { + $description = "Technical notes:\r\n" . $game->notes + . "\r\n----\r\n" + . $description; + } + // http://cweiske.de/ouya-store-api-docs.htm#get-https-devs-ouya-tv-api-v1-details - return [ + $data = [ 'type' => 'Game', 'title' => $game->title, - 'description' => $game->description, + 'description' => $description, 'gamerNumbers' => $game->players, 'genres' => $game->genres, @@ -348,14 +652,122 @@ function buildDetails($game) number_format($latestRelease->size / 1024 / 1024, 2, '.', '') . ' MiB', ], - 'tileImage' => $game->media->discover, + 'tileImage' => $game->discover, 'mediaTiles' => $mediaTiles, 'mobileAppIcon' => null, 'heroImage' => [ 'url' => null, ], - 'promotedProduct' => null, + 'promotedProduct' => $product, + 'buttons' => $buttons, + + 'stouyapi' => [ + 'internet-archive' => $iaUrl, + 'developer-url' => $game->developer->website ?? null, + ] + ]; + + if ($linkDeveloperPage) { + $data['developer']['url'] = 'ouya://launcher/discover/dev--' + . categoryPath($game->developer->uuid); + } + + return $data; +} + +function buildDeveloperCurrentGamer() +{ + return [ + 'gamer' => [ + 'uuid' => '00702342-0000-1111-2222-c3e1500cafe2', + 'username' => 'stouyapi', + ], + ]; +} + +/** + * For /api/v1/developers/xxx/products/?only=yyy + */ +function buildDeveloperProductOnly($product, $developer) +{ + return [ + 'developerName' => $developer->name, + 'currency' => $product->currency, + 'products' => [ + buildProduct($product), + ], + ]; +} + +/** + * For /api/v1/developers/xxx/products/ + */ +function buildDeveloperProducts($products, $developer) +{ + //remove duplicates + $products = array_values(array_column($products, null, 'identifier')); + + $jsonProducts = []; + foreach ($products as $product) { + $jsonProducts[] = buildProduct($product); + } + return [ + 'developerName' => $developer->name, + 'currency' => $products[0]->currency ?? 'EUR', + 'products' => $jsonProducts, + ]; +} + +function buildPurchases($game) +{ + $purchasesData = [ + 'purchases' => [], + ]; + $promotedProduct = getPromotedProduct($game); + if ($promotedProduct) { + $purchasesData['purchases'][] = [ + 'purchaseDate' => time() * 1000, + 'generateDate' => time() * 1000, + 'identifier' => $promotedProduct->identifier, + 'gamer' => '00702342-0000-1111-2222-c3e1500cafe2',//gamer uuid + 'uuid' => '00702342-0000-1111-2222-c3e1500beef3',//transaction ID + 'priceInCents' => $promotedProduct->originalPrice * 100, + 'localPrice' => $promotedProduct->localPrice, + 'currency' => $promotedProduct->currency, + ]; + } + + $encryptedOnce = dummyEncrypt($purchasesData); + $encryptedTwice = dummyEncrypt($encryptedOnce); + return $encryptedTwice; +} + +function buildSearch($games) +{ + $games = sortByTitle($games); + $results = []; + foreach ($games as $game) { + $results[] = [ + 'title' => $game->title, + 'url' => 'ouya://launcher/details?app=' . $game->packageName, + 'contentRating' => $game->contentRating, + ]; + } + return [ + 'count' => count($results), + 'results' => $results, + ]; +} + +function dummyEncrypt($data) +{ + return [ + 'key' => base64_encode('0123456789abcdef'), + 'iv' => 't3jir1LHpICunvhlM76edQ==',//random bytes + 'blob' => base64_encode( + json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) + ), ]; } @@ -372,12 +784,12 @@ function addChunkedDiscoverRows(&$data, $games, $title) } } -function addDiscoverRow(&$data, $title, $games) +function addDiscoverRow(&$data, $title, $games, $ranked = false) { $row = [ 'title' => $title, - 'showPrice' => false, - 'ranked' => false, + 'showPrice' => true, + 'ranked' => $ranked, 'tiles' => [], ]; foreach ($games as $game) { @@ -388,6 +800,11 @@ function addDiscoverRow(&$data, $title, $games) } else { //game + if (isset($game->links->original)) { + //do not link unlocked games. + // people an access them via the original games + continue; + } $tilePos = findTile($data['tiles'], $game->packageName); if ($tilePos === null) { $tilePos = count($data['tiles']); @@ -441,20 +858,16 @@ function buildDiscoverGameTile($game) 'updated_at' => strtotime($latestRelease->date), 'updatedAt' => $latestRelease->date, 'title' => $game->title, - 'image' => $game->media->discover, + 'image' => $game->discover, 'contentRating' => $game->contentRating, 'rating' => [ 'count' => $game->rating->count, 'average' => $game->rating->average, ], + 'promotedProduct' => buildProduct(getPromotedProduct($game)), ]; } -function categoryPath($title) -{ - return str_replace(['/', '\\', ' ', '+'], '_', $title); -} - function getAllAges($games) { $ages = []; @@ -475,6 +888,8 @@ function getAllGenres($games) function addMissingGameProperties($game) { + global $cfgEnableQr; + if (!isset($game->overview)) { $game->overview = null; } @@ -513,9 +928,14 @@ function addMissingGameProperties($game) $game->rating->count = 0; } + $game->firstRelease = null; $game->latestRelease = null; + $firstReleaseTimestamp = null; $latestReleaseTimestamp = 0; foreach ($game->releases as $release) { + if (isset($release->broken) && $release->broken) { + continue; + } if (!isset($release->publicSize)) { $release->publicSize = 0; } @@ -528,17 +948,24 @@ function addMissingGameProperties($game) $game->latestRelease = $release; $latestReleaseTimestamp = $releaseTimestamp; } + if ($firstReleaseTimestamp === null + || $releaseTimestamp < $firstReleaseTimestamp + ) { + $game->firstRelease = $release; + $firstReleaseTimestamp = $releaseTimestamp; + } + } + if ($game->firstRelease === null) { + error('No first release for ' . $game->packageName); } if ($game->latestRelease === null) { error('No latest release for ' . $game->packageName); } - if (!isset($game->media->video)) { - $game->media->video = null; - } - if (!isset($game->media->screenshots)) { - $game->media->screenshots = []; + if (!isset($game->media)) { + $game->media = []; } + if (!isset($game->developer->uuid)) { $game->developer->uuid = null; } @@ -554,22 +981,154 @@ function addMissingGameProperties($game) if (!isset($game->developer->founder)) { $game->developer->founder = false; } + + if ($cfgEnableQr && $game->website) { + $qrfileName = preg_replace('#[^\\w\\d._-]#', '_', $game->website) . '.png'; + $qrfilePath = $GLOBALS['qrDir'] . $qrfileName; + if (!file_exists($qrfilePath)) { + $cmd = __DIR__ . '/create-qr.sh' + . ' ' . escapeshellarg($game->website) + . ' ' . escapeshellarg($qrfilePath); + passthru($cmd, $retval); + if ($retval != 0) { + exit(20); + } + } + $qrUrlPath = $GLOBALS['baseUrl'] . 'gen-qr/' . $qrfileName; + $game->media[] = (object) [ + 'type' => 'image', + 'url' => $qrUrlPath, + ]; + } + + //rewrite urls from Internet Archive to our servers + $game->discover = rewriteUrl($game->discover); + foreach ($game->media as $medium) { + $medium->url = rewriteUrl($medium->url); + } + foreach ($game->releases as $release) { + $release->url = rewriteUrl($release->url); + } +} + +/** + * Implements a sensible ranking system described in + * https://stackoverflow.com/a/1411268/2826013 + */ +function calculateRank(array $games) +{ + $averageRatings = array_map( + function ($game) { + return $game->rating->average; + }, + $games + ); + $average = array_sum($averageRatings) / count($averageRatings); + $C = $average; + $m = 500; + + foreach ($games as $game) { + $R = $game->rating->average; + $v = $game->rating->count; + $game->rating->rank = ($R * $v + $C * $m) / ($v + $m); + } +} + +function getFirstVideoUrl($media) +{ + foreach ($media as $medium) { + if ($medium->type == 'video') { + return $medium->url; + } + } + return null; +} + +function getAllImageUrls($media) +{ + $imageUrls = []; + foreach ($media as $medium) { + if ($medium->type == 'image') { + $imageUrls[] = $medium->url; + } + } + return $imageUrls; +} + +function getPromotedProduct($game) +{ + if (!isset($game->products) || !count($game->products)) { + return null; + } + foreach ($game->products as $gameProd) { + if ($gameProd->promoted) { + return $gameProd; + } + } + return null; +} + +/** + * vimeo only work with HTTPS now, + * and the OUYA does not support SNI. + * We get SSL errors and no video for them :/ + */ +function isUnsupportedVideoUrl($url) +{ + return strpos($url, '://vimeo.com/') !== false; +} + +function removeMakeGames(array $games) +{ + return filterByGenre($games, 'Tutorials', true); +} + +function removeMakeGenres($genres) +{ + $filtered = []; + foreach ($genres as $genre) { + if ($genre != 'Tutorials' && $genre != 'Builds') { + $filtered[] = $genre; + } + } + return $filtered; +} + +function rewriteUrl($url) +{ + foreach ($GLOBALS['urlRewrites'] as $pattern => $replacement) { + $url = preg_replace($pattern, $replacement, $url); + } + return $url; } function writeJson($path, $data) { - global $wwwDir; + global $cfgMini, $wwwDir; $fullPath = $wwwDir . $path; $dir = dirname($fullPath); if (!is_dir($dir)) { mkdir($dir, 0777, true); } + $opts = JSON_UNESCAPED_SLASHES; + if (!$cfgMini) { + $opts |= JSON_PRETTY_PRINT; + } file_put_contents( $fullPath, - json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n" + json_encode($data, $opts) . "\n" ); } +function writeCategoryJson($path, $data) +{ + writeJson($path, $data); + + $forgePath = str_replace('.json', '.forge.json', $path); + $forgeData = convertCategoryToForge($data, true); + writeJson($forgePath, $forgeData); +} + function error($msg) { fwrite(STDERR, $msg . "\n");