commit
7afa2f350d
8 changed files with 1786 additions and 0 deletions
@ -0,0 +1,152 @@ |
|||||||
|
// index.js
|
||||||
|
|
||||||
|
const puppeteer = require('puppeteer'); |
||||||
|
const { PuppeteerScreenRecorder } = require('puppeteer-screen-recorder'); |
||||||
|
const ffmpeg = require('fluent-ffmpeg'); |
||||||
|
const getVideoDurationInSeconds = require('get-video-duration').getVideoDurationInSeconds; |
||||||
|
|
||||||
|
async function rotateVideo(inputPath, outputPath) { |
||||||
|
// Get original duration
|
||||||
|
const originalDuration = await getVideoDurationInSeconds(inputPath); |
||||||
|
const targetDuration = 30; |
||||||
|
const speedFactor = originalDuration / targetDuration; |
||||||
|
|
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
ffmpeg(inputPath) |
||||||
|
.videoFilters([ |
||||||
|
{ filter: 'crop', options: '1920:1080:0:0' }, |
||||||
|
{ filter: 'transpose', options: '2' }, |
||||||
|
{ filter: 'setpts', options: `PTS/${speedFactor}` } |
||||||
|
]) |
||||||
|
// Optional: speed up audio as well (max 2x per atempo, so chain if needed)
|
||||||
|
.audioFilters( |
||||||
|
speedFactor <= 2 |
||||||
|
? { filter: 'atempo', options: speedFactor } |
||||||
|
: [ |
||||||
|
{ filter: 'atempo', options: 2 }, |
||||||
|
{ filter: 'atempo', options: speedFactor / 2 } |
||||||
|
] |
||||||
|
) |
||||||
|
.save(outputPath) |
||||||
|
.on('end', () => { |
||||||
|
console.log('Crop, rotation, and speed-up complete!'); |
||||||
|
resolve(); |
||||||
|
}) |
||||||
|
.on('error', err => reject(err)); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function addMusicToVideo(videoPath, musicPath, outputPath, musicVolume = 0.1) { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
ffmpeg() |
||||||
|
.addInput(videoPath) |
||||||
|
.addInput(musicPath) |
||||||
|
.complexFilter([ |
||||||
|
// If your video has NO audio, just set the music volume and map
|
||||||
|
'[1:a]volume=' + musicVolume + '[aud]' |
||||||
|
]) |
||||||
|
.outputOptions([ |
||||||
|
'-map 0:v', // video from first input
|
||||||
|
'-map [aud]', // audio from [aud]
|
||||||
|
'-shortest', // end when shortest ends
|
||||||
|
'-c:v copy' // copy video, do not re-encode
|
||||||
|
]) |
||||||
|
.save(outputPath) |
||||||
|
.on('end', () => { |
||||||
|
console.log('Music added and volume set!'); |
||||||
|
resolve(); |
||||||
|
}) |
||||||
|
.on('error', reject); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(async () => { |
||||||
|
const browser = await puppeteer.launch({ headless: false }); |
||||||
|
const page = await browser.newPage(); |
||||||
|
|
||||||
|
// Set to vertical/portrait mode
|
||||||
|
await page.setViewport({ |
||||||
|
width: 1920, |
||||||
|
height: 1080, |
||||||
|
deviceScaleFactor: 1, |
||||||
|
}); |
||||||
|
|
||||||
|
await page.goto('https://tacticfront.io/join/aQoyBBX7'); |
||||||
|
|
||||||
|
// Click "Pause game" on load
|
||||||
|
await page.waitForSelector('button[title="Pause game"]'); |
||||||
|
await page.click('button[title="Pause game"]'); |
||||||
|
console.log('Game paused. Waiting for unpause (second click)...'); |
||||||
|
|
||||||
|
// Set up a promise in Node
|
||||||
|
const clickedPromise = new Promise(resolve => { |
||||||
|
page.exposeFunction('notifyPauseButtonClicked', resolve); |
||||||
|
}); |
||||||
|
|
||||||
|
// Attach a page-wide click handler in the browser context
|
||||||
|
await page.evaluate(() => { |
||||||
|
// Remove any existing listener for safety
|
||||||
|
window._pauseButtonHandler && document.removeEventListener('click', window._pauseButtonHandler); |
||||||
|
|
||||||
|
window._pauseButtonHandler = function (e) { |
||||||
|
if ( |
||||||
|
e.target && |
||||||
|
e.target.tagName === 'BUTTON' && |
||||||
|
e.target.title === 'Pause game' |
||||||
|
) { |
||||||
|
window.notifyPauseButtonClicked(); |
||||||
|
document.removeEventListener('click', window._pauseButtonHandler); // Only once
|
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
document.addEventListener('click', window._pauseButtonHandler); |
||||||
|
}); |
||||||
|
|
||||||
|
console.log('Game paused. Waiting for unpause (manual click)...'); |
||||||
|
await clickedPromise; |
||||||
|
|
||||||
|
|
||||||
|
// Start recording
|
||||||
|
console.log("Recorder Start") |
||||||
|
const recorder = new PuppeteerScreenRecorder(page); |
||||||
|
await recorder.start('./replay.mp4'); |
||||||
|
|
||||||
|
// Stop recording on game end
|
||||||
|
let recordingStopped = false; |
||||||
|
async function finishRecording() { |
||||||
|
if (!recordingStopped) { |
||||||
|
recordingStopped = true; |
||||||
|
await recorder.stop(); |
||||||
|
await browser.close(); |
||||||
|
// Now rotate the video
|
||||||
|
await rotateVideo('replay.mp4', 'output_vertical.mp4'); |
||||||
|
await addMusicToVideo('output_vertical.mp4', './music/a-hero-of-the-80s-126684.mp3', 'final_output.mp4', 0.05); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
page.on('console', async msg => { |
||||||
|
if (msg.text().includes('local server ending game')) { |
||||||
|
console.log('Detected "local server ending game" in console. Stopping recording...'); |
||||||
|
await finishRecording(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
setTimeout(async () => { |
||||||
|
if (!recordingStopped) { |
||||||
|
console.log('Timeout reached. Stopping recording...'); |
||||||
|
await finishRecording(); |
||||||
|
} |
||||||
|
}, 10 * 60 * 1000); // 5 minutes
|
||||||
|
|
||||||
|
})(); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Spina has won the game
|
||||||
|
//local server ending game
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@ |
|||||||
|
https://pixabay.com/music/search/genre/video%20games/?pagi=2 |
||||||
@ -0,0 +1,17 @@ |
|||||||
|
{ |
||||||
|
"name": "replayrecorder", |
||||||
|
"version": "1.0.0", |
||||||
|
"main": "index.js", |
||||||
|
"scripts": { |
||||||
|
"test": "echo \"Error: no test specified\" && exit 1" |
||||||
|
}, |
||||||
|
"author": "", |
||||||
|
"license": "ISC", |
||||||
|
"description": "", |
||||||
|
"dependencies": { |
||||||
|
"fluent-ffmpeg": "^2.1.3", |
||||||
|
"get-video-duration": "^4.1.0", |
||||||
|
"puppeteer": "^24.10.2", |
||||||
|
"puppeteer-screen-recorder": "^3.0.6" |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue