video-providers.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /**
  2. * Video Generation Service -- routes to provider adapters
  3. */
  4. import type {
  5. VideoProviderId,
  6. VideoGenerationConfig,
  7. VideoGenerationOptions,
  8. VideoGenerationResult,
  9. VideoProviderConfig,
  10. } from './types';
  11. import { generateWithSeedance, testSeedanceConnectivity } from './adapters/seedance-adapter';
  12. import { generateWithKling, testKlingConnectivity } from './adapters/kling-adapter';
  13. import { generateWithVeo, testVeoConnectivity } from './adapters/veo-adapter';
  14. import {
  15. generateWithMiniMaxVideo,
  16. testMiniMaxVideoConnectivity,
  17. } from './adapters/minimax-video-adapter';
  18. import { generateWithGrokVideo, testGrokVideoConnectivity } from './adapters/grok-video-adapter';
  19. export const VIDEO_PROVIDERS: Record<VideoProviderId, VideoProviderConfig> = {
  20. seedance: {
  21. id: 'seedance',
  22. name: 'Seedance',
  23. requiresApiKey: true,
  24. defaultBaseUrl: 'https://ark.cn-beijing.volces.com',
  25. models: [
  26. { id: 'doubao-seedance-1-5-pro-251215', name: 'Seedance 1.5 Pro' },
  27. { id: 'doubao-seedance-1-0-pro-250528', name: 'Seedance 1.0 Pro' },
  28. {
  29. id: 'doubao-seedance-1-0-pro-fast-251015',
  30. name: 'Seedance 1.0 Pro Fast',
  31. },
  32. {
  33. id: 'doubao-seedance-1-0-lite-t2v-250428',
  34. name: 'Seedance 1.0 Lite T2V',
  35. },
  36. ],
  37. supportedAspectRatios: ['16:9', '4:3', '1:1', '9:16', '3:4', '21:9'],
  38. supportedDurations: [5, 10],
  39. supportedResolutions: ['480p', '720p', '1080p'],
  40. maxDuration: 10,
  41. },
  42. kling: {
  43. id: 'kling',
  44. name: 'Kling',
  45. requiresApiKey: true,
  46. defaultBaseUrl: 'https://api-beijing.klingai.com',
  47. models: [
  48. { id: 'kling-v2-6', name: 'Kling V2.6' },
  49. { id: 'kling-v1-6', name: 'Kling V1.6' },
  50. ],
  51. supportedAspectRatios: ['16:9', '1:1', '9:16'],
  52. supportedDurations: [5, 10],
  53. maxDuration: 10,
  54. },
  55. veo: {
  56. id: 'veo',
  57. name: 'Veo',
  58. requiresApiKey: true,
  59. defaultBaseUrl: 'https://generativelanguage.googleapis.com',
  60. models: [
  61. { id: 'veo-3.1-fast-generate-001', name: 'Veo 3.1 Fast' },
  62. { id: 'veo-3.1-generate-001', name: 'Veo 3.1' },
  63. { id: 'veo-3.0-fast-generate-001', name: 'Veo 3.0 Fast' },
  64. { id: 'veo-3.0-generate-001', name: 'Veo 3.0' },
  65. { id: 'veo-2.0-generate-001', name: 'Veo 2.0' },
  66. ],
  67. supportedAspectRatios: ['16:9', '1:1', '9:16'],
  68. supportedDurations: [8],
  69. supportedResolutions: ['720p'],
  70. maxDuration: 8,
  71. },
  72. sora: {
  73. id: 'sora',
  74. name: 'Sora',
  75. requiresApiKey: true,
  76. models: [],
  77. supportedAspectRatios: ['16:9', '1:1', '9:16'],
  78. maxDuration: 20,
  79. },
  80. 'minimax-video': {
  81. id: 'minimax-video',
  82. name: 'MiniMax Video',
  83. requiresApiKey: true,
  84. defaultBaseUrl: 'https://api.minimaxi.com',
  85. models: [
  86. { id: 'MiniMax-Hailuo-2.3', name: 'Hailuo 2.3' },
  87. { id: 'MiniMax-Hailuo-2.3-Fast', name: 'Hailuo 2.3 Fast' },
  88. { id: 'MiniMax-Hailuo-02', name: 'Hailuo 02' },
  89. { id: 'T2V-01-Director', name: 'T2V-01 Director' },
  90. { id: 'T2V-01', name: 'T2V-01' },
  91. ],
  92. supportedAspectRatios: ['16:9', '4:3', '1:1', '9:16'],
  93. supportedDurations: [6, 10],
  94. supportedResolutions: ['720p', '1080p'],
  95. maxDuration: 10,
  96. },
  97. 'grok-video': {
  98. id: 'grok-video',
  99. name: 'Grok Video (xAI)',
  100. requiresApiKey: true,
  101. defaultBaseUrl: 'https://api.x.ai/v1',
  102. models: [{ id: 'grok-imagine-video', name: 'Grok Imagine Video' }],
  103. supportedAspectRatios: ['16:9', '1:1', '9:16'],
  104. supportedDurations: [6],
  105. maxDuration: 6,
  106. },
  107. };
  108. export async function testVideoConnectivity(
  109. config: VideoGenerationConfig,
  110. ): Promise<{ success: boolean; message: string }> {
  111. switch (config.providerId) {
  112. case 'seedance':
  113. return testSeedanceConnectivity(config);
  114. case 'kling':
  115. return testKlingConnectivity(config);
  116. case 'veo':
  117. return testVeoConnectivity(config);
  118. case 'minimax-video':
  119. return testMiniMaxVideoConnectivity(config);
  120. case 'grok-video':
  121. return testGrokVideoConnectivity(config);
  122. default:
  123. return {
  124. success: false,
  125. message: `Unsupported video provider: ${config.providerId}`,
  126. };
  127. }
  128. }
  129. /**
  130. * Normalize video generation options against provider capabilities.
  131. * Ensures duration, aspectRatio, and resolution are valid for the given provider.
  132. * Falls back to the first supported value when the requested value is unsupported.
  133. */
  134. export function normalizeVideoOptions(
  135. providerId: VideoProviderId,
  136. options: VideoGenerationOptions,
  137. ): VideoGenerationOptions {
  138. const provider = VIDEO_PROVIDERS[providerId];
  139. if (!provider) return options;
  140. const normalized = { ...options };
  141. // Duration: use first supported value if unset or unsupported
  142. if (provider.supportedDurations && provider.supportedDurations.length > 0) {
  143. if (!normalized.duration || !provider.supportedDurations.includes(normalized.duration)) {
  144. normalized.duration = provider.supportedDurations[0];
  145. }
  146. }
  147. // Aspect ratio: use first supported value if unset or unsupported
  148. if (provider.supportedAspectRatios && provider.supportedAspectRatios.length > 0) {
  149. if (
  150. !normalized.aspectRatio ||
  151. !provider.supportedAspectRatios.includes(normalized.aspectRatio)
  152. ) {
  153. normalized.aspectRatio = provider
  154. .supportedAspectRatios[0] as VideoGenerationOptions['aspectRatio'];
  155. }
  156. }
  157. // Resolution: use first supported value if unset or unsupported
  158. if (provider.supportedResolutions && provider.supportedResolutions.length > 0) {
  159. if (!normalized.resolution || !provider.supportedResolutions.includes(normalized.resolution)) {
  160. normalized.resolution = provider.supportedResolutions[0];
  161. }
  162. }
  163. return normalized;
  164. }
  165. export async function generateVideo(
  166. config: VideoGenerationConfig,
  167. options: VideoGenerationOptions,
  168. ): Promise<VideoGenerationResult> {
  169. switch (config.providerId) {
  170. case 'seedance':
  171. return generateWithSeedance(config, options);
  172. case 'kling':
  173. return generateWithKling(config, options);
  174. case 'veo':
  175. return generateWithVeo(config, options);
  176. case 'minimax-video':
  177. return generateWithMiniMaxVideo(config, options);
  178. case 'grok-video':
  179. return generateWithGrokVideo(config, options);
  180. default:
  181. throw new Error(`Unsupported video provider: ${config.providerId}`);
  182. }
  183. }