-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
364 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
# mse-2025-template | ||
# mse-2025-template |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# Screen Recorder Extension (Прототип) | ||
|
||
Это расширение для браузера Chrome, которое позволяет записывать экран и звук с микрофона. Записи сохраняются локально, а также отправляются на сервер. | ||
|
||
--- | ||
|
||
## Как запустить проект | ||
|
||
### Предварительные требования | ||
|
||
1. **Установите Docker и Docker Compose.** | ||
|
||
2. **Склонируйте папку `playground`**. | ||
|
||
--- | ||
|
||
### Запуск проекта | ||
|
||
1. **Перейдите в папку `server`**: | ||
```bash | ||
cd server | ||
``` | ||
|
||
2. **Запустите проект с помощью Docker Compose**: | ||
```bash | ||
docker-compose up --build | ||
``` | ||
|
||
3. **Установите расширение в Google Chrome**: | ||
- Откройте `chrome://extensions/`. | ||
- Включите режим разработчика. | ||
- Нажмите "Load unpacked" (Загрузить распакованное расширение) и выберите папку `client`. | ||
|
||
--- | ||
|
||
### Использование расширения | ||
|
||
1. **Нажмите на иконку расширения**: | ||
- Откроется вкладка для записи экрана. | ||
|
||
2. **Введите имя пользователя**: | ||
- В поле "Имя пользователя" введите ваше имя. | ||
|
||
3. **Начните запись**: | ||
- Нажмите кнопку "Начать запись". | ||
- Выберите экран или окно для записи, а также предоставьте доступ к микрофону. | ||
|
||
4. **Остановите запись**: | ||
- Нажмите кнопку "Остановить запись". | ||
- Видео сохранится на ваш компьютер и отправится на сервер. | ||
|
||
--- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
chrome.action.onClicked.addListener((tab) => { | ||
chrome.tabs.create({ url: chrome.runtime.getURL("index.html") }); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<!DOCTYPE html> | ||
<html lang="ru"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=700, height=600, initial-scale=1.0"> | ||
<title>Screen Recorder</title> | ||
<link rel="icon" type="image/png" href="static/icon.png"> | ||
<link rel="stylesheet" type="text/css" href="style.css"> | ||
</head> | ||
<body> | ||
<div class="container"> | ||
<h1>Screen Recorder</h1> | ||
<div class="input-group"> | ||
<label for="username">Имя пользователя:</label> | ||
<input type="text" id="username" placeholder="№гр_Фамилия_Имя"> | ||
</div> | ||
<button id="recordButton">Начать запись</button> | ||
</div> | ||
<script src="index.js"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
document.getElementById('recordButton').onclick = async function () { | ||
const username = document.getElementById('username').value; | ||
if (!username) { | ||
alert('Пожалуйста, введите ваше имя.'); | ||
return; | ||
} | ||
|
||
if (this.textContent === 'Начать запись') { | ||
localStorage.setItem('username', username); | ||
|
||
// Запрашиваем доступ к экрану и микрофону | ||
chrome.desktopCapture.chooseDesktopMedia(["screen", "window"], async (streamId) => { | ||
if (!streamId) { | ||
alert('Запись экрана отменена.'); | ||
return; | ||
} | ||
|
||
try { | ||
// Получаем медиапоток с экрана и микрофона | ||
const screenStream = await navigator.mediaDevices.getUserMedia({ | ||
audio: false, | ||
video: { | ||
mandatory: { | ||
chromeMediaSource: 'desktop', | ||
chromeMediaSourceId: streamId | ||
} | ||
} | ||
}); | ||
|
||
const microphoneStream = await navigator.mediaDevices.getUserMedia({ | ||
audio: true, | ||
video: false | ||
}); | ||
|
||
// Объединяем потоки | ||
const combinedStream = new MediaStream([ | ||
...screenStream.getVideoTracks(), | ||
...microphoneStream.getAudioTracks() | ||
]); | ||
|
||
// Создаем MediaRecorder | ||
mediaRecorder = new MediaRecorder(combinedStream, { | ||
mimeType: 'video/webm; codecs=vp9,opus' | ||
}); | ||
mediaRecorder.ondataavailable = handleDataAvailable; | ||
mediaRecorder.start(); | ||
startTime = new Date(); | ||
|
||
this.textContent = 'Остановить запись'; | ||
this.classList.add('stop-button'); | ||
} catch (error) { | ||
console.error('Ошибка при получении доступа к медиаустройствам:', error); | ||
alert('Не удалось начать запись. Проверьте разрешения.'); | ||
} | ||
}); | ||
} else { | ||
// Останавливаем запись | ||
if (mediaRecorder && mediaRecorder.state === 'recording') { | ||
mediaRecorder.stop(); | ||
this.textContent = 'Начать запись'; | ||
this.classList.remove('stop-button'); | ||
} | ||
} | ||
}; | ||
|
||
let mediaRecorder; | ||
let recordedChunks = []; | ||
let startTime; | ||
|
||
// Обработчик данных записи | ||
function handleDataAvailable(event) { | ||
if (event.data.size > 0) { | ||
recordedChunks.push(event.data); | ||
const username = localStorage.getItem('username'); | ||
const formattedDate = formatDate(startTime); | ||
const filename = `${username}_${formattedDate}.webm`; | ||
const blob = new Blob(recordedChunks, { type: 'video/webm' }); | ||
|
||
saveVideoLocally(blob, filename); | ||
sendVideoToServer(blob, filename); | ||
recordedChunks = []; | ||
} | ||
} | ||
|
||
// Форматирование даты | ||
function formatDate(date) { | ||
const year = date.getFullYear(); | ||
const month = String(date.getMonth() + 1).padStart(2, '0'); | ||
const day = String(date.getDate()).padStart(2, '0'); | ||
const hours = String(date.getHours()).padStart(2, '0'); | ||
const minutes = String(date.getMinutes()).padStart(2, '0'); | ||
const seconds = String(date.getSeconds()).padStart(2, '0'); | ||
return `${year}-${month}-${day}_${hours}-${minutes}-${seconds}`; | ||
} | ||
|
||
// Сохранение видео локально | ||
function saveVideoLocally(blob, filename) { | ||
const url = URL.createObjectURL(blob); | ||
const a = document.createElement('a'); | ||
a.style.display = 'none'; | ||
a.href = url; | ||
a.download = filename; | ||
document.body.appendChild(a); | ||
a.click(); | ||
setTimeout(() => { | ||
document.body.removeChild(a); | ||
window.URL.revokeObjectURL(url); | ||
}, 100); | ||
} | ||
|
||
// Отправка видео на сервер | ||
function sendVideoToServer(blob, filename) { | ||
let formData = new FormData(); | ||
formData.append('file', blob, filename); | ||
fetch('http://localhost:5000/upload', { | ||
method: 'POST', | ||
body: formData | ||
}).then(response => response.json()) | ||
.then(success => { | ||
console.log('Видео успешно загружено:', success); | ||
}).catch(error => { | ||
console.error('Ошибка при загрузке видео:', error); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
{ | ||
"manifest_version": 3, | ||
"name": "Screen Recorder", | ||
"version": "1.0", | ||
"permissions": ["desktopCapture", "storage", "audioCapture"], | ||
"background": { | ||
"service_worker": "background.js" | ||
}, | ||
"action": { | ||
"default_icon": { | ||
"16": "static/icon.png", | ||
"48": "static/icon.png", | ||
"128": "static/icon.png" | ||
} | ||
}, | ||
"web_accessible_resources": [ | ||
{ | ||
"resources": ["index.html", "style.css", "index.js", "static/icon.png"], | ||
"matches": ["<all_urls>"] | ||
} | ||
] | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
body { | ||
margin: 0; | ||
padding: 0; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
height: 100vh; | ||
background-color: #f9f9f9; | ||
font-family: Arial, sans-serif; | ||
} | ||
|
||
.container { | ||
text-align: center; | ||
width: 100%; | ||
max-width: 400px; | ||
padding: 20px; | ||
box-sizing: border-box; | ||
} | ||
|
||
h1 { | ||
font-size: 24px; | ||
font-weight: bold; | ||
margin-bottom: 20px; | ||
} | ||
|
||
.input-group { | ||
margin-bottom: 20px; | ||
text-align: left; | ||
} | ||
|
||
label { | ||
font-size: 16px; | ||
font-weight: bold; | ||
display: block; | ||
margin-bottom: 5px; | ||
} | ||
|
||
input { | ||
width: 100%; | ||
padding: 10px; | ||
font-size: 16px; | ||
border: 1px solid #ccc; | ||
border-radius: 5px; | ||
box-sizing: border-box; | ||
} | ||
|
||
button { | ||
width: 100%; | ||
padding: 15px; | ||
font-size: 18px; | ||
color: white; | ||
background-color: #4CAF50; | ||
border: none; | ||
border-radius: 5px; | ||
cursor: pointer; | ||
transition: background-color 0.3s ease; | ||
} | ||
|
||
button:hover { | ||
opacity: 0.9; | ||
} | ||
|
||
button.stop-button { | ||
background-color: #ff4d4d; | ||
} | ||
|
||
button:disabled { | ||
background-color: #ccc; | ||
cursor: not-allowed; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
FROM python:3.10-slim | ||
|
||
WORKDIR /app | ||
|
||
COPY requirements.txt . | ||
|
||
RUN pip install --no-cache-dir -r requirements.txt | ||
|
||
COPY . . | ||
|
||
CMD ["python", "app.py"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from flask import Flask, request, jsonify | ||
from pymongo import MongoClient | ||
import os | ||
|
||
app = Flask(__name__) | ||
|
||
client = MongoClient('mongodb://mongo:27017/') | ||
db = client.screen_recorder | ||
|
||
@app.route('/upload', methods=['POST']) | ||
def upload_file(): | ||
if 'file' not in request.files: | ||
return jsonify({'error': 'No file part'}), 400 | ||
|
||
file = request.files['file'] | ||
if file.filename == '': | ||
return jsonify({'error': 'No selected file'}), 400 | ||
|
||
try: | ||
file_path = os.path.join('/data', file.filename) | ||
file.save(file_path) | ||
|
||
db.recordings.insert_one({'filename': file.filename, 'path': file_path}) | ||
|
||
return jsonify({'success': True}), 200 | ||
except Exception as e: | ||
print(f"Ошибка при сохранении файла или записи в базу данных: {e}") | ||
return jsonify({'error': 'Internal server error'}), 500 | ||
|
||
if __name__ == '__main__': | ||
app.run(host='0.0.0.0', port=5000) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
version: '3' | ||
services: | ||
mongo: | ||
image: mongo:8.0 | ||
container_name: mongo | ||
volumes: | ||
- mongo_data:/data/db | ||
networks: | ||
- backend | ||
|
||
app: | ||
build: . | ||
ports: | ||
- "5000:5000" | ||
volumes: | ||
- ./data:/data | ||
depends_on: | ||
- mongo | ||
networks: | ||
- backend | ||
|
||
volumes: | ||
mongo_data: | ||
|
||
networks: | ||
backend: | ||
driver: bridge |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
flask==3.1.0 | ||
flask-pymongo==3.0.1 |