File size: 5,267 Bytes
f64828c
a99b4ac
 
07ae658
 
a99b4ac
f64828c
 
f7f7c46
 
 
 
 
 
 
 
 
 
07ae658
7e58471
faac575
7e58471
07ae658
f7f7c46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
07ae658
f64828c
 
07ae658
f7f7c46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6997aba
 
 
 
 
f7f7c46
 
 
 
 
f64828c
444bbdb
07ae658
f7f7c46
66f11df
 
 
 
f7f7c46
 
 
 
 
 
 
 
 
 
 
 
 
6fa9239
f7f7c46
 
 
 
 
 
 
7e58471
07ae658
f7f7c46
f64828c
1f50b60
 
 
f64828c
f7f7c46
 
f132701
f7f7c46
a99b4ac
f7f7c46
0cf8825
 
f7f7c46
0cf8825
 
f7f7c46
 
0cf8825
a99b4ac
f7f7c46
 
a99b4ac
f7f7c46
 
66f11df
f7f7c46
a99b4ac
 
07ae658
 
 
66f11df
7e58471
 
 
 
 
 
 
 
 
66f11df
 
 
 
 
7e58471
 
 
 
07ae658
 
f7f7c46
 
 
 
07ae658
 
f7f7c46
07ae658
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
import { v4 as uuid } from 'uuid'
import { upscaleVideo } from './upscaleVideo.mts'
import { keepVideo } from './keepVideo.mts'

import { getStats } from './getStats.mts'
import { enhanceVideo } from './enhanceVideo.mts'
import { callZeroscope } from './callZeroscope.mts'
import { downloadVideo } from './downloadVideo.mts'
import { getDatabase } from './getDatabase.mts'
import { callMusicgen } from './callMusicgen.mts'

let hasReachedStartingPoint = false

type RunMode = 'running' | 'paused' | 'dry_run'

const status = `${process.env.WEBTV_STATUS || 'dry_run'}` as RunMode

console.log(`Web TV server status: ${status}`)

// to add more diversity to the stream, let's cut down on the length
const maxShotsPerSequence = 10

const main = async () => {
  if (status === 'paused') {
    setTimeout(() => {
      main()
    }, 30000)
    return
  }

  console.log('Reading persistent file structure..')
  const stats = await getStats()
  console.log(`New format: We have ${stats.nbVideoFiles} video files`)

  console.log(`Legacy: We have ${stats.nbLegacyVideoFiles} video files and ${stats.nbLegacyAudioFiles} audio files`)

  console.log('Reading prompt database..')
  const db = await getDatabase('./database.json')

  const nbTotalShots = db.sequences.reduce((a, s) => a + s.shots.length, 0)
  console.log(`Prompt database version: ${db.version}`)
  console.log(`We got ${db.sequences.length} sequences for ${nbTotalShots} shots in total`)

  console.log('Generating videos sequences..')
  const instanceId = process.env.WEBTV_WORKER_INSTANCE_ID || '0'

  const startingPointExists = db.sequences.some(seq => seq.shots.some(shot => shot.shotId === db.startAtShotId))
  
  if (!startingPointExists) {
    console.log(`Starting point ${db.startAtShotId} not found, we will start at the beginning`)
    hasReachedStartingPoint = true
  } else if (db.startAtShotId) {
    console.log(`We are going to start at shot ${db.startAtShotId}`)
  } else {
    console.log('We are going to start at the beginning')
  }

  for (const sequence of db.sequences) {
    const containsStartingPoint = sequence.shots.some(shot => shot.shotId === db.startAtShotId)

    // we skip sequences that were already processed
    if (!hasReachedStartingPoint && !containsStartingPoint) {
      continue
    }

    // some sequences can also be skipped by human curation
    if (sequence.skip) {
      continue
    }

    console.log(`
-----------------------------------------------------------
Going to generate ${sequence.shots.length} for prompt:
${sequence.videoPrompt}
`)

    const movieId = uuid()

    const generatedShots: string[] = []

    // this is hardcoded everywhere for now, since videos longer than 3 sec require the Nvidia A100
    const videoDurationInSecs = 3

    let shotIndex = 0

    for (const shot of sequence.shots) {

      if (shot.shotId === db.startAtShotId) {
        hasReachedStartingPoint = true
      }

      if (!hasReachedStartingPoint) {
        shotIndex++
        continue
      }

      console.log(`- generating shot ${shot.shotId}: ${shot.videoPrompt}`)

      if (status === 'dry_run') {
        // console.log('DRY RUN')
        shotIndex++
        continue
      }


      try {
        const generatedVideoUrl = await callZeroscope(shot.videoPrompt)

        // note that we need to use the shot INDEX (not just the ID) 
        // to make sure the order is respected
        const shotFileName = `inst_${instanceId}_movie_${movieId}_seq_${sequence.sequenceId}_shot_index_${shotIndex++}_shot_${shot.shotId}_${Date.now()}.mp4`

        console.log(`- downloading shot ${shotFileName} from ${generatedVideoUrl}`)
        await downloadVideo(generatedVideoUrl, shotFileName)

        console.log(`- downloaded shot ${shotFileName}`)

        console.log('- upscaling shot..')
      
        try {
          await upscaleVideo(shotFileName, shot.videoPrompt)
        } catch (err) {
          // upscaling is finicky, if it fails we try again
          console.log('- trying again to upscale shot..')
          await upscaleVideo(shotFileName, shot.videoPrompt)
        }

        console.log('- enhancing shot..')
        await enhanceVideo(shotFileName)

        console.log('- saving final shot..')
        await keepVideo(shotFileName, process.env.WEBTV_VIDEO_STORAGE_PATH_NEXT)

        generatedShots.push(shotFileName)

        console.log('- done!')
      } catch (err) {
        console.log(`- error: ${err}`)
      }

      // for the initial demo, we may want to limit the number of shots per sequence
      if (shotIndex > maxShotsPerSequence) {
        break
      }
    }

    console.log('Finished generating sequence')
    
    const totalRunTime = videoDurationInSecs * generatedShots.length

      if (totalRunTime <= 0) {
        continue
      }

    // TODO: generate music from MusicGen, with the correct length
    // (or we could generate a slightly larger track and let ffmpeg cut it)
    console.log(`TODO: generate ${totalRunTime} seconds of music`)
    await callMusicgen(sequence.audioPrompt) // this does nothing for now
  }

  console.log('Finished the cycle')

  hasReachedStartingPoint = true // set this to true in all cases

  setTimeout(() => {
    main()
  }, 2000)
}

setTimeout(() => {
  main()
}, 3000)