categories.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import { Video, OrionVideo, Category, Schedule } from "./types";
  2. import fs from "fs";
  3. import axios from "axios";
  4. import moment from "moment";
  5. import dotenv from "dotenv";
  6. dotenv.config();
  7. const orionHeader = process.env.orionHeader;
  8. const orionUrl = `https://orion.joystream.org/graphql`;
  9. const videosFile = "./videos.json";
  10. const scheduleFile = "./schedule.json";
  11. if (!orionHeader) {
  12. console.log(`Please set orionHeader in .env`);
  13. process.exit(1);
  14. }
  15. const getDay = (daysFromNow: number = 0) =>
  16. moment().add(daysFromNow, "days").format("YYYY-MM-DD");
  17. export const getOrionVideo = ({ videoId }: Video): OrionVideo => {
  18. return {
  19. videoId,
  20. videoCutUrl: `https://eu-central-1.linodeobjects.com/atlas-featured-content/category-featured-videos/1/video-cut-${videoId}.mp4`,
  21. };
  22. };
  23. const loadSchedule = () => require(scheduleFile);
  24. const loadVideos = (): { videos: Video[]; categories: number[] } => {
  25. // load videos and extract category IDs
  26. const categories: number[] = [];
  27. const videos: Video[] = require(videosFile);
  28. videos.forEach(
  29. ({ categoryId }) =>
  30. categories.includes(categoryId) || categories.push(categoryId)
  31. );
  32. return { videos, categories };
  33. };
  34. const selectVideos = (videos: Video[], count: number): OrionVideo[] => {
  35. let selected: OrionVideo[] = [];
  36. for (let n = 0; n < count; ++n) {
  37. // remove selected videos
  38. const available: Video[] = videos.filter(
  39. ({ videoId }) => !selected.find((v) => v.videoId === videoId)
  40. );
  41. // select random video
  42. const id = Math.floor(Math.random() * available.length);
  43. if (available.length && available[id])
  44. selected.push(getOrionVideo(available[id]));
  45. else break;
  46. }
  47. return selected;
  48. };
  49. const generateVideoList = (file: string) => {
  50. console.log(`Indexing video files ..`);
  51. const videos = fs
  52. .readFileSync(`./list`, `utf-8`)
  53. .split(`\n`)
  54. .reduce((list: Video[], path: string): Video[] => {
  55. const match = path.match(/\.\/([^\/]+) (\d+)\/(\d+)-(\d+)\.mp4/);
  56. if (match) {
  57. const category = match[1];
  58. const count = Number(match[2]);
  59. const categoryId = Number(match[3]);
  60. const videoId = Number(match[4]);
  61. const video: Video = { category, count, categoryId, videoId };
  62. return list.concat(video);
  63. } else return list;
  64. }, []);
  65. fs.writeFileSync(file, JSON.stringify(videos));
  66. console.log(`Wrote ${videos.length} videos to ${file}`);
  67. };
  68. const generateSchedule = (maxDays: number = 7): Schedule => {
  69. const loaded = loadVideos();
  70. const days: number[] = [];
  71. for (let day = 0; day <= maxDays; ++day) {
  72. days.push(day);
  73. }
  74. const left: Video[][] = [];
  75. const schedule: Schedule = {};
  76. days.forEach((day) => {
  77. schedule[getDay(day)] = {
  78. categories: loaded.categories.map((categoryId: number) => {
  79. const available: Video[] = left[categoryId]
  80. ? left[categoryId]
  81. : loaded.videos.filter((v) => v.categoryId === categoryId);
  82. const videos: OrionVideo[] = selectVideos(available, 3); // select n videos
  83. left[categoryId] = available.filter(
  84. ({ videoId }) => !videos.find((v) => v.videoId === videoId)
  85. );
  86. return { categoryId, videos };
  87. }),
  88. };
  89. });
  90. return schedule;
  91. };
  92. const getCategoryFeaturedVideos = (): Promise<string> => {
  93. const data = {
  94. query:
  95. "query GetCategoriesFeaturedVideos {\n allCategoriesFeaturedVideos {\n categoryId\n videos {\n videoId\n videoCutUrl\n }\n }\n}",
  96. };
  97. return axios
  98. .post(orionUrl, data)
  99. .then(({ data }: any) => {
  100. fs.writeFileSync(`featured.json`, JSON.stringify(data));
  101. console.log(`Wrote featured.json`);
  102. return data;
  103. })
  104. .catch((error: any) => error.message + error);
  105. };
  106. const setCategoryVideos = (categoryId: number): string =>
  107. `
  108. mutation SetFeaturedVideos ($videos: [FeaturedVideoInput!]!) {
  109. setCategoryFeaturedVideos(categoryId: "${categoryId}", videos: $videos) {
  110. videoId
  111. videoCutUrl
  112. }
  113. }
  114. `;
  115. const setCategoryFeaturedVideos = async (
  116. categoryId: number,
  117. videos: OrionVideo[]
  118. ) => {
  119. const headers = { Authorization: orionHeader };
  120. const query = setCategoryVideos(categoryId);
  121. axios
  122. .post(orionUrl, { query, variables: { videos } }, { headers })
  123. .then(async ({ status, statusText }: any) =>
  124. console.log(`Set videos for category ${categoryId}:`, status, statusText)
  125. )
  126. .catch((error: any) => {
  127. console.error(
  128. `Failed to set featured videos for category ${categoryId}:`,
  129. error.response.data.errors.map(
  130. (e: any) => e.message + JSON.stringify(e.locations)
  131. )
  132. );
  133. process.exit(1);
  134. });
  135. };
  136. const main = async (args: string[]) => {
  137. switch (args[0]) {
  138. case "get":
  139. getCategoryFeaturedVideos();
  140. break;
  141. case "set":
  142. try {
  143. const schedule: Schedule = require(scheduleFile);
  144. if (!schedule || !schedule[getDay()]) {
  145. console.error(`Current day not found in schedule. Run update again.`);
  146. process.exit(1);
  147. }
  148. const { categories } = schedule[getDay()];
  149. categories
  150. .sort((a, b) => a.categoryId - b.categoryId)
  151. .map(({ categoryId, videos }) =>
  152. setCategoryFeaturedVideos(categoryId, videos)
  153. );
  154. } catch (e) {
  155. console.warn(`Did you run: yarn run schedule`);
  156. }
  157. break;
  158. case "schedule":
  159. fs.writeFileSync(scheduleFile, JSON.stringify(generateSchedule()));
  160. console.log(`Wrote schedule to ${scheduleFile}.`);
  161. break;
  162. case "update":
  163. generateVideoList(videosFile);
  164. break;
  165. default:
  166. console.log(`Available commands: [get, set, schedule, update]`);
  167. }
  168. };
  169. main(process.argv.slice(2));