π‘ Π§ΡΠΎ ΠΌΡ ΡΠ΄Π΅Π»Π°Π»ΠΈ
ΠΠ°ΡΠ° ΡΠ΅Π»Ρ β ΡΠΎΠ±ΡΠ°ΡΡ ΡΠΊΡΠΈΠΏΡ, ΠΊΠΎΡΠΎΡΡΠΉ:
- ΠΏΡΠΈΠ½ΠΈΠΌΠ°Π΅Ρ Π²ΠΈΠ΄Π΅ΠΎ ΠΈ ΠΌΡΠ·ΡΠΊΡ Π½Π° Π²Ρ ΠΎΠ΄Π΅;
- Π½Π°ΡΠ΅Π·Π°Π΅Ρ ΠΊΠ»ΠΈΠΏΡ ΠΏΠΎ Π±ΠΈΡΠ°ΠΌ ΠΌΡΠ·ΡΠΊΠΈ;
- ΡΠΊΠ»Π΅ΠΈΠ²Π°Π΅Ρ Π² ΠΎΠ΄Π½ΠΎ Π²ΠΈΠ΄Π΅ΠΎ;
- ΡΠΊΡΠΏΠΎΡΡΠΈΡΡΠ΅Ρ Π³ΠΎΡΠΎΠ²ΡΡ ΡΡΠΎΡΠΈΠ·.
Π Π°Π±ΠΎΡΠΈΠΉ ΠΏΡΠΎΡΠΎΡΠΈΠΏ ΠΌΡ ΡΠ΄Π΅Π»Π°Π»ΠΈ Π·Π° Π΄Π΅Π½Ρ. Π‘ΠΊΡΠΈΠΏΡ Π½Π° Node.js + ffmpeg.
π§± Π‘ΡΠ΅ΠΊ
- Node.js β ΠΎΡΠ½ΠΎΠ²Π°
- ffmpeg β ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π²ΠΈΠ΄Π΅ΠΎ ΠΈ Π°ΡΠ΄ΠΈΠΎ
- fs-extra β ΡΠ°Π±ΠΎΡΠ° Ρ ΡΠ°ΠΉΠ»Π°ΠΌΠΈ
- fluent-ffmpeg β ΡΠ΄ΠΎΠ±Π½Π°Ρ ΠΎΠ±ΡΡΡΠΊΠ° Π΄Π»Ρ ffmpeg
- child_process β Π΄Π»Ρ Π·Π°ΠΏΡΡΠΊΠ° shell-ΠΊΠΎΠΌΠ°Π½Π΄
1. ΠΠ°Π΄Π°ΡΠΌ Π±Π°Π·ΠΎΠ²ΡΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ Π΄Π»Ρ Π½Π°ΡΠ΅Π·ΠΊΠΈ
π― ΠΡΠΎ Π½ΡΠΆΠ½ΠΎ, ΡΡΠΎΠ±Ρ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ, Π½Π° ΠΊΠ°ΠΊΠΈΠ΅ Π²ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΠΎΡΡΠ΅Π·ΠΊΠΈ Π±ΡΠ΄Π΅ΠΌ Π½Π°ΡΠ΅Π·Π°ΡΡ Π²ΠΈΠ΄Π΅ΠΎ β Π² Π½Π°ΡΠ΅ΠΌ ΡΠ»ΡΡΠ°Π΅ ΠΏΠΎ Π±ΠΈΡΠ°ΠΌ ΠΌΡΠ·ΡΠΊΠΈ.
const bpm = 85; // ΠΠ°Π΄Π°ΡΠΌ ΡΠ΅ΠΌΠΏ ΠΌΡΠ·ΡΠΊΠΈ (BPM)
const beatsPerSegment = 2; // ΠΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ Π±ΠΈΡΠΎΠ² Π½Π° ΠΊΠ°ΠΆΠ΄ΡΠΉ ΡΠ΅Π³ΠΌΠ΅Π½Ρ
const beatsPerVideo = 4; // ΠΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ Π±ΠΈΡΠΎΠ² Π½Π° ΠΊΠ°ΠΆΠ΄ΠΎΠ΅ Π²ΠΈΠ΄Π΅ΠΎ
// Π Π°ΡΡΠ΅Ρ Π΄Π»ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΠΈ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΡΠ΅Π³ΠΌΠ΅Π½ΡΠ°
const secondsPerBeat = 60 / bpm;
const secondsPerSegment = secondsPerBeat * beatsPerSegment;
console.log(`ΠΠ»ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΡΠ΅Π³ΠΌΠ΅Π½ΡΠ°: ${secondsPerSegment} ΡΠ΅ΠΊΡΠ½Π΄`);
2. ΠΠΎΠ»ΡΡΠ°Π΅ΠΌ Π΄Π»ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ Π°ΡΠ΄ΠΈΠΎΡΡΠ΅ΠΊΠ°
π― Π§ΡΠΎΠ±Ρ Π·Π½Π°ΡΡ, ΡΠΊΠΎΠ»ΡΠΊΠΎ Π±ΠΈΡΠΎΠ² Π²Π»Π΅Π·Π°Π΅Ρ Π² ΡΡΠ΅ΠΊ β ΠΈ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²Π΅Π½Π½ΠΎ, ΡΠΊΠΎΠ»ΡΠΊΠΎ Π½Π°ΡΠ΅Π·ΠΎΠΊ Π½ΡΠΆΠ½ΠΎ.
// ΠΠ»ΠΈΠ½Π° ΠΌΡΠ·ΡΠΊΠ°Π»ΡΠ½ΠΎΠ³ΠΎ ΡΡΠ΅ΠΊΠ°
const trackDuration = execSync(
`ffmpeg -i ${musicFile} 2>&1 | grep "Duration"`
)
.toString()
.match(/Duration: (\d+):(\d+):(\d+\.\d+)/);
if (!trackDuration) {
console.error("ΠΠ΅ ΡΠ΄Π°Π»ΠΎΡΡ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ Π΄Π»ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ ΡΡΠ΅ΠΊΠ°.");
process.exit(1);
}
const [_, trackHours, trackMinutes, trackSeconds] = trackDuration.map(Number);
const totalTrackSeconds =
trackHours * 3600 + trackMinutes * 60 + trackSeconds;
console.log(`ΠΠ±ΡΠ°Ρ Π΄Π»ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ ΡΡΠ΅ΠΊΠ° Π² ΡΠ΅ΠΊΡΠ½Π΄Π°Ρ
: ${totalTrackSeconds}`);
3. ΠΡΡΠΈΡΠ»ΡΠ΅ΠΌ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ Π½ΡΠΆΠ½ΡΡ ΡΡΠ°Π³ΠΌΠ΅Π½ΡΠΎΠ²
π― ΠΡΠΎΡΡΠΎ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ beat-ΠΈΠ½ΡΠ΅ΡΠ²Π°Π»ΠΎΠ², Π½Π° ΠΊΠΎΡΠΎΡΡΠ΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΡΠ΅Π·Π°ΡΡ Π²Π΅ΡΡ ΡΡΠ΅ΠΊ.
// Π Π°ΡΡΠ΅Ρ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²Π° ΡΠ΅Π³ΠΌΠ΅Π½ΡΠΎΠ²
const segmentCount =
beatsPerVideo || Math.floor(totalTrackSeconds / secondsPerSegment);
console.log(`ΠΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΡΠ΅Π³ΠΌΠ΅Π½ΡΠΎΠ²: ${segmentCount}`);
4. ΠΠΎΠ΄Π³ΠΎΡΠ°Π²Π»ΠΈΠ²Π°Π΅ΠΌ ΡΡΡΠ°ΠΆΠΈ Π΄Π»Ρ Π½Π°ΡΠ΅Π·ΠΊΠΈ
π― ΠΠ°Ρ ΠΎΠ΄ΠΈΠΌ ΠΏΠΎΠ΄Ρ ΠΎΠ΄ΡΡΠΈΠ΅ ΠΏΠΎ Π΄Π»ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΠΈ Π²ΠΈΠ΄Π΅ΠΎ Π΄Π»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΡΠ΅Π³ΠΌΠ΅Π½ΡΠΎΠ².
// ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΡΠΏΠΈΡΠΊΠ° Π²ΡΠ΅Ρ
ΡΡΡΠ°ΠΆΠ΅ΠΉ ΠΈ ΠΈΡ
ΡΠΎΡΡΠΈΡΠΎΠ²ΠΊΠ°
let footageFiles = fs
.readdirSync(footageFolder)
.filter((file) => file.endsWith(".mp4"))
.sort();
// Π€ΠΈΠ»ΡΡΡΠ°ΡΠΈΡ Π²ΠΈΠ΄Π΅ΠΎ ΠΏΠΎ Π΄Π»ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΠΈ
footageFiles = await Promise.all(
footageFiles.map(async (file) => {
const filePath = path.join(footageFolder, file);
const duration = await getVideoDuration(filePath);
return duration >= secondsPerSegment ? filePath : null;
})
);
footageFiles = footageFiles.filter((file) => file !== null);
console.log(`ΠΠ°ΠΉΠ΄Π΅Π½ΠΎ ΠΏΠΎΠ΄Ρ
ΠΎΠ΄ΡΡΠΈΡ
ΡΡΡΠ°ΠΆΠ΅ΠΉ: ${footageFiles.length}`);
5. ΠΠ°ΡΠ΅Π·Π°Π΅ΠΌ ΠΊΠ»ΠΈΠΏΡ ΠΈΠ· ΠΈΡΡ ΠΎΠ΄Π½ΡΡ Π²ΠΈΠ΄Π΅ΠΎ
π― ΠΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ ΡΠ΅ΡΠ΅Π΄ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΊΠ»ΠΈΠΏΠΎΠ², Π²ΡΠ±ΠΈΡΠ°Ρ ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°ΡΠ΅Π»ΡΠ½ΠΎ ΠΈΠ· Π΄ΠΎΡΡΡΠΏΠ½ΡΡ ΡΡΡΠ°ΠΆΠ΅ΠΉ.
// Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΡΠ½ΠΈΠΊΠ°Π»ΡΠ½ΡΡ
ΡΠ΅Π³ΠΌΠ΅Π½ΡΠΎΠ² ΠΈΠ· ΡΡΡΠ°ΠΆΠ΅ΠΉ
const segments = [];
for (let i = 0; i < segmentCount; i++) {
const footageIndex = i % footageFiles.length;
const footageFile = footageFiles[footageIndex];
const segmentFile = path.join(outputFolder, `segment${i}.mp4`);
await new Promise((resolve, reject) => {
console.log(`ΠΠ°ΡΠ°Π»ΠΎ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ ΡΠ΅Π³ΠΌΠ΅Π½ΡΠ° ${i} ΠΈΠ· ΡΠ°ΠΉΠ»Π° ${footageFile}...`);
ffmpeg()
.input(footageFile)
.output(segmentFile)
.outputOptions([
`-ss 0`,
`-t ${secondsPerSegment}`,
`-r 30`, // ΡΡΡΠ°Π½ΠΎΠ²ΠΊΠ° ΡΡΠ΅ΠΉΠΌΡΠ΅ΠΉΡΠ° Π΄Π»Ρ ΠΏΡΠ΅Π΄ΠΎΡΠ²ΡΠ°ΡΠ΅Π½ΠΈΡ Π·Π°Π²ΠΈΡΠ°Π½ΠΈΠΉ
`-vcodec libx264`, // ΡΡΡΠ°Π½ΠΎΠ²ΠΊΠ° Π²ΠΈΠ΄Π΅ΠΎ ΠΊΠΎΠ΄Π΅ΠΊΠ° Π΄Π»Ρ ΡΠ½ΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ
`-an`, // ΠΎΡΠΊΠ»ΡΡΠΈΡΡ Π°ΡΠ΄ΠΈΠΎ
`-fflags +genpts`, // Π³Π΅Π½Π΅ΡΠ°ΡΠΈΡ Π²ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ
ΠΌΠ΅ΡΠΎΠΊ
])
.on("progress", (progress) => {
console.log(`Π‘Π΅Π³ΠΌΠ΅Π½Ρ ${i} ΠΏΡΠΎΠ³ΡΠ΅ΡΡ: ${progress.percent}%`);
})
.on("end", () => {
console.log(`Π‘Π΅Π³ΠΌΠ΅Π½Ρ ${i} Π·Π°Π²Π΅ΡΡΠ΅Π½`);
segments.push(segmentFile);
resolve();
})
.on("error", (err) => {
console.error(`ΠΡΠΈΠ±ΠΊΠ° ΠΏΡΠΈ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠ΅ ΡΠ΅Π³ΠΌΠ΅Π½ΡΠ° ${i}: ${err}`);
reject(err);
})
.run();
});
}
6. Π‘ΠΊΠ»Π΅ΠΈΠ²Π°Π΅ΠΌ ΡΡΠ°Π³ΠΌΠ΅Π½ΡΡ Π² ΠΎΠ΄ΠΈΠ½ ΡΠ°ΠΉΠ»
π― FFmpeg ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ ΡΠΊΠ»Π΅ΠΈΡΡ ΠΌΠ½ΠΎΠ³ΠΎ ΡΠ°ΠΉΠ»ΠΎΠ², Π΅ΡΠ»ΠΈ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡ ΠΈΠΌ ΡΠΏΠΈΡΠΎΠΊ.
// Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΡΠ°ΠΉΠ»Π° ΡΠΎ ΡΠΏΠΈΡΠΊΠΎΠΌ ΡΠ΅Π³ΠΌΠ΅Π½ΡΠΎΠ²
const concatList = segments
.map((file) => `file '${path.resolve(file)}'`)
.join("\n");
fs.writeFileSync(path.join(outputFolder, "concat_list.txt"), concatList);
// ΠΠ±ΡΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠ΅ Π²ΡΠ΅Ρ
ΡΠ΅Π³ΠΌΠ΅Π½ΡΠΎΠ² Π² ΠΎΠ΄Π½ΠΎ Π²ΠΈΠ΄Π΅ΠΎ Π±Π΅Π· Π°ΡΠ΄ΠΈΠΎ
await new Promise((resolve, reject) => {
const concatListPath = path.join(outputFolder, "concat_list.txt");
ffmpeg()
.input(concatListPath)
.inputOptions(["-f concat", "-safe 0"])
.outputOptions([
"-c:v libx264", // ΠΠ΅ΡΠ΅ΠΊΠΎΠ΄ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π² ΠΎΠ΄ΠΈΠ½ ΠΊΠΎΠ΄Π΅ΠΊ Π΄Π»Ρ ΠΊΠΎΠ½ΡΠΈΡΡΠ΅Π½ΡΠΈΠΈ
"-pix_fmt yuv420p", // ΠΠ±Π΅ΡΠΏΠ΅ΡΠ΅Π½ΠΈΠ΅ ΡΠΎΠ²ΠΌΠ΅ΡΡΠΈΠΌΠΎΡΡΠΈ Ρ Π±ΠΎΠ»ΡΡΠΈΠ½ΡΡΠ²ΠΎΠΌ ΠΏΠ»Π΅Π΅ΡΠΎΠ²
"-fflags +genpts", // Π³Π΅Π½Π΅ΡΠ°ΡΠΈΡ Π²ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ
ΠΌΠ΅ΡΠΎΠΊ
])
.output(path.join(outputFolder, "output_without_audio.mp4"))
.on("end", () => {
console.log("ΠΠΈΠ΄Π΅ΠΎ Π±Π΅Π· Π°ΡΠ΄ΠΈΠΎ ΡΠΎΠ·Π΄Π°Π½ΠΎ: output_without_audio.mp4");
resolve();
})
.run();
});
7. ΠΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ ΠΌΡΠ·ΡΠΊΡ ΠΊ Π³ΠΎΡΠΎΠ²ΠΎΠΌΡ Π²ΠΈΠ΄Π΅ΠΎ
π― ΠΠ° Π²ΡΡ ΠΎΠ΄Π΅ Ρ Π½Π°Ρ ΡΠΆΠ΅ ΡΠΊΠ»Π΅Π΅Π½Π½ΠΎΠ΅ Π²ΠΈΠ΄Π΅ΠΎ, ΠΎΡΡΠ°Π»ΠΎΡΡ Π½Π°Π»ΠΎΠΆΠΈΡΡ ΠΌΡΠ·ΡΠΊΡ ΠΈ ΠΎΠ±ΡΠ΅Π·Π°ΡΡ ΠΏΠΎ ΠΊΠΎΡΠΎΡΠΊΠΎΠΌΡ ΡΡΠ΅ΠΊΡ.
// ΠΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π°ΡΠ΄ΠΈΠΎ ΠΊ Π²ΠΈΠ΄Π΅ΠΎ ΠΈ ΠΎΠ±ΡΠ΅Π·ΠΊΠ° Π²ΠΈΠ΄Π΅ΠΎ Π΄ΠΎ Π΄Π»ΠΈΠ½Ρ Π°ΡΠ΄ΠΈΠΎ
await new Promise((resolve, reject) => {
console.log("ΠΠ°ΡΠ°Π»ΠΎ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΡ Π°ΡΠ΄ΠΈΠΎ...");
ffmpeg()
.input(path.join(outputFolder, "output_without_audio.mp4"))
.input(musicFile)
.outputOptions([
"-c:v copy",
"-c:a aac",
"-strict experimental",
"-shortest", // ΠΡΡΠ΅Π·Π°Π΅Ρ Π²ΠΈΠ΄Π΅ΠΎ Π΄ΠΎ Π΄Π»ΠΈΠ½Ρ Π°ΡΠ΄ΠΈΠΎ
])
.output(path.join(outputFolder, "final_video.mp4"))
.on("progress", (progress) => {
console.log(`ΠΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π°ΡΠ΄ΠΈΠΎ ΠΏΡΠΎΠ³ΡΠ΅ΡΡ: ${progress.percent}%`);
})
.on("end", () => {
console.log("ΠΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π°ΡΠ΄ΠΈΠΎ Π·Π°Π²Π΅ΡΡΠ΅Π½ΠΎ");
resolve();
})
.run();
});
console.log("Π€ΠΈΠ½Π°Π»ΡΠ½ΠΎΠ΅ Π²ΠΈΠ΄Π΅ΠΎ Ρ Π°ΡΠ΄ΠΈΠΎ ΡΠΎΠ·Π΄Π°Π½ΠΎ: final_video.mp4");
β Π§ΡΠΎ Π² ΠΈΡΠΎΠ³Π΅
- 1 ΠΊΠ»ΠΈΠΊ β Π΄ΠΎ 85 ΡΡΠ°Π³ΠΌΠ΅Π½ΡΠΎΠ² β Π³ΠΎΡΠΎΠ²Π°Ρ ΡΡΠΎΡΠΈΠ·
- 2-3 ΠΌΠΈΠ½ΡΡΡ Π³Π΅Π½Π΅ΡΠ°ΡΠΈΠΈ Π²ΠΌΠ΅ΡΡΠΎ ΡΠ°ΡΠΎΠ² ΡΡΡΠ½ΠΎΠ³ΠΎ ΠΌΠΎΠ½ΡΠ°ΠΆΠ°
- ΠΠΎΠ½ΡΠ΅Π½Ρ Π²ΡΡ ΠΎΠ΄ΠΈΡ ΡΠ΅Π³ΡΠ»ΡΡΠ½ΠΎ, Π±Π΅Π· ΠΏΡΠΎΠ²ΠΎΠ»ΠΎΡΠ΅ΠΊ
- ΠΠ»ΠΈΠ΅Π½Ρ ΡΡΠ°ΡΠΈΡ 0 ΡΡΠ±Π»Π΅ΠΉ Π½Π° ΠΌΠΎΠ½ΡΠ°ΠΆ
π§ Π§ΡΠΎ ΠΌΠΎΠΆΠ½ΠΎ ΡΠ»ΡΡΡΠΈΡΡ
- ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ΅ ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ BPM Π²ΠΌΠ΅ΡΡΠΎ ΡΡΡΠ½ΠΎΠ³ΠΎ Π²Π²ΠΎΠ΄Π°
- Π Π°ΡΠΏΠΎΠ·Π½Π°Π²Π°Π½ΠΈΠ΅ «ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠ½ΡΡ » ΠΌΠΎΠΌΠ΅Π½ΡΠΎΠ² ΠΏΠΎ Π·Π²ΡΠΊΡ ΠΈΠ»ΠΈ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΡ Π² ΠΊΠ°Π΄ΡΠ΅
- UI Π½Π° React: Π²ΡΠ±ΡΠ°ΡΡ ΠΏΠ°ΠΏΠΊΡ Ρ ΠΊΠ»ΠΈΠΏΠ°ΠΌΠΈ, ΠΌΡΠ·ΡΠΊΡ, Π½Π°ΠΆΠ°ΡΡ ΠΊΠ½ΠΎΠΏΠΊΡ β Π³ΠΎΡΠΎΠ²ΠΎ
- ΠΠ°Π³ΡΡΠ·ΠΊΠ° ΡΠ΅ΡΠ΅Π· Telegram-Π±ΠΎΡΠ° ΠΈΠ»ΠΈ web-ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ
π© ΠΡΡΡ Π·Π°Π΄Π°ΡΠΈ, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΆΡΡΡ Π²ΡΠ΅ΠΌΡ ΠΈ ΡΠΈΠ»Ρ?
ΠΠ°Π²Π°ΠΉΡΠ΅ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΠ·ΠΈΡΡΠ΅ΠΌ. ΠΠ°ΠΏΠΈΡΠΈΡΠ΅ Π½Π°ΠΌ β ΠΏΡΠ΅Π΄Π»ΠΎΠΆΠΈΠΌ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΠΎΠ΅ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅.