此内容根据文章生成,仅用于文章内容的解释与总结
-
当你的电脑无法通过视频线连接到电视机,可能是由于线缆长度不足或者接口不兼容,而你的电视机恰好支持浏览器功能。
-
当你在外面参加培训或交流活动,现场只提供了一个WiFi网络。大家刚刚熟悉,马上就要开始屏幕分享,你需要一个快速的方式让大家都能看到你的屏幕。
这个时候,你就需要一个工具来捕获和分享你的屏幕和音频(包括设备音频和麦克风 输入),并通过网页形式与他人共享。这样,观众无需下载任何会议软件,仅需打开浏览器即可观看。
当然有时候你需要提供必要的文件,比如代码,文档等。所以这个程序还允许你上传与下载文件。上传的文件会保存在当前目录下的upload
文件夹中,你也可以从upload
文件夹中下载文件。
例如一个伙伴上传了文件,另一个伙伴可以从这个地址下载文件。这个项目我做了一次较大的调整,原本是用python编写的屏幕和麦克风捕获,为了跨平台使用的同时保持代码的简洁,使用了JS重构了代码,使用通用的浏览器获取本地权限的方式,希望这个项目对你有帮助。
项目开源地址:https://github.com/jiangyangcreate/WebScreenShare.git
安装依赖
pip install flask
项目目录结构
项目下的文件夹:
templates 文件夹中包含一个 upload.html 文件,用于页面展示。
uploads 文件夹用于保存上传的文件。
static 文件夹用于放置js代码逻辑:检测摄像头是否存在。
your_project/
├── templates/
│ └── upload.html
│ └── stream.html
├── uploads/
├── static/
└── app.py
核心代码
替换上传表单的样式
upload.html
<!doctype html>
<html lang="zh-CN">
<head>
<style>
/* 美化上传按钮 */
.file-upload {
position: relative;
display: inline-block;
overflow: hidden;
cursor: pointer;
background-color: #ff6600;
color: #fff;
padding: 10px 20px;
border: none;
border-radius: 5px;
transition: background-color 0.3s;
font-size: 16px;
}
.file-upload:hover {
background-color: #e65c00;
}
.file-upload input[type="file"] {
position: absolute;
font-size: 18px;
right: 0;
top: 0;
opacity: 0;
cursor: pointer;
width: 100%;
height: 100%;
}
/* 显示文件名 */
.file-name {
margin-top: 10px;
font-size: 14px;
color: #f0f0f0;
}
input[type="submit"] {
background-color: #ff6600;
color: #fff;
border: none;
padding: 10px 20px;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s;
font-size: 16px;
margin-top: 10px;
}
input[type="submit"]:hover {
background-color: #e65c00;
}
</style>
<!-- 引入设备检测的 JavaScript 文件 -->
<script src="{{ url_for('static', filename='js/deviceDetection.js') }}"></script>
<script>
// 显示选中的文件名
function displayFileName(input) {
const fileNameElement = document.getElementById('file-name');
if (input.files.length > 0) {
fileNameElement.textContent = `已选择文件: ${input.files[0].name}`;
} else {
fileNameElement.textContent = '';
}
}
// 显示上传表单
window.onload = function() {
const uploadContainer = document.getElementById('upload-form-container');
const uploadForm = uploadContainer.querySelector('form');
uploadForm.style.display = 'flex';
}
</script>
</head>
<body>
<h2>上传新文件</h2>
<div id="upload-form-container">
<form method="post" enctype="multipart/form-data">
<label class="file-upload">
上传文件
<input type="file" name="file" required accept="image/png, image/jpeg, image/gif, image/jpg" onchange="displayFileName(this)">
</label>
<div class="file-name" id="file-name"></div>
<input type="submit" value="确认">
</form>
</div>
</body>
</html>
映射路由与文件允许范围代码
app.py
# app.py
from flask import Flask, request, redirect, url_for, send_from_directory, render_template
import os
app = Flask(__name__)
# 设置上传文件夹
UPLOAD_FOLDER = 'uploads'
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
# 允许的文件扩展名
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/')
def index():
return render_template('index.html')
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
# 检查是否有文件上传
if 'file' not in request.files:
return redirect(request.url)
file = request.files['file']
if file.filename == '':
return redirect(request.url)
if file and allowed_file(file.filename):
filename = file.filename
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return redirect(url_for('upload_file'))
# 显示上传表单和文件列表
files = os.listdir(app.config['UPLOAD_FOLDER'])
return render_template('upload.html', files=files)
@app.route('/uploads/<filename>')
def download_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
# 新增的路由:设备选择页面
@app.route('/media_selection')
def media_selection():
return render_template('media_selection.html')
# 新增的路由:媒体接口页面
@app.route('/media_interface')
def media_interface():
# 获取用户选择的参数
screen = request.args.get('screen') == 'on'
camera = request.args.get('camera') == 'on'
sound = request.args.get('sound') == 'on'
# 至少选择一项已经在前端验证,这里不再重复
return render_template('media_interface.html', screen=screen, camera=camera, sound=sound)
if __name__ == '__main__':
app.run(debug=True,host='0.0.0.0',port=8080)
使用浏览器接口设备检测
deviceDetection.js
// 检测摄像头是否存在
function hasCamera() {
return navigator.mediaDevices.enumerateDevices()
.then(devices => {
return devices.some(device => device.kind === 'videoinput');
})
.catch(err => {
console.error('Error detecting camera:', err);
return false;
});
}
// 检测麦克风是否存在
function hasMicrophone() {
return navigator.mediaDevices.enumerateDevices()
.then(devices => {
return devices.some(device => device.kind === 'audioinput');
})
.catch(err => {
console.error('Error detecting microphone:', err);
return false;
});
}
// 显示检测结果
function displayDeviceStatus() {
hasCamera().then(camera => {
const cameraStatus = document.getElementById('camera-status');
if (camera) {
cameraStatus.textContent = '摄像头: 已连接';
cameraStatus.style.color = 'green';
} else {
cameraStatus.textContent = '摄像头: 未连接';
cameraStatus.style.color = 'red';
}
});
hasMicrophone().then(microphone => {
const microphoneStatus = document.getElementById('microphone-status');
if (microphone) {
microphoneStatus.textContent = '麦克风: 已连接';
microphoneStatus.style.color = 'green';
} else {
microphoneStatus.textContent = '麦克风: 未连接';
microphoneStatus.style.color = 'red';
}
});
}
// 当页面加载完成后执行
window.addEventListener('load', displayDeviceStatus);
function detectDevices() {
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
console.log('浏览器不支持设备检测');
return;
}
navigator.mediaDevices.enumerateDevices()
.then(function(devices) {
let hasCamera = false;
let hasMicrophone = false;
devices.forEach(function(device) {
if (device.kind === 'videoinput') {
hasCamera = true;
}
if (device.kind === 'audioinput') {
hasMicrophone = true;
}
});
const cameraCheckbox = document.getElementById('camera-checkbox');
const soundCheckbox = document.getElementById('sound-checkbox');
if (hasCamera) {
cameraCheckbox.disabled = false;
} else {
cameraCheckbox.disabled = true;
cameraCheckbox.checked = false;
}
if (hasMicrophone) {
soundCheckbox.disabled = false;
} else {
soundCheckbox.disabled = true;
soundCheckbox.checked = false;
}
})
.catch(function(err) {
console.error('设备检测时出错:', err);
});
}
// 当页面内容加载完毕后执行设备检测
document.addEventListener('DOMContentLoaded', detectDevices);
总结
浏览器是现在网络应用的标配,使用浏览器接口获取设备权限,可以实现简单优雅的跨平台使用。每种语言都有自己的优势,Python的生态强大,JS的灵活性高,两者结合可以实现很多有趣的应用。