极简短视频查看、删除应用

本地短视频服务器

背景:我的NAS中存放了很多短视频,多到很多没看过,于是写了这个程序来随机查看并删除短视频

运行:

安装依赖后运行main.py

直接使用docker:

docker pull realwang/short_video

docker run -d -p 3000:3000 -v /path/to/your/video:/app/video realwang/short_video

功能

  • 扫描本地视频和图片,并在网页上显示
  • 在网页上 点赞和删除文件
  • 上下滑动来切换文件
  • 媒体文件放在video目录下
  • 本地数据库

代码由4个文件组成

1.数据库操作
复制代码
# database.py

import sqlite3

def init_db():
    conn = sqlite3.connect('media.db')
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS media
                 (id INTEGER PRIMARY KEY, name TEXT, path TEXT, views INTEGER DEFAULT 0, likes INTEGER DEFAULT 0)''')
    conn.commit()
    conn.close()

def add_media(name, path):
    try:
        conn = sqlite3.connect('media.db')
        c = conn.cursor()
        c.execute("INSERT INTO media (name, path) VALUES (?, ?)", (name, path))
        conn.commit()
        conn.close()
        return True
    except sqlite3.IntegrityError:
        # 处理重复键错误等数据库约束错误
        return False
    except Exception as e:
        print(f"Error inserting {name} into database: {str(e)}")
        return False

def update_views(media_id):
    conn = sqlite3.connect('media.db')
    c = conn.cursor()
    c.execute("UPDATE media SET views = views + 1 WHERE id = ?", (media_id,))
    conn.commit()
    conn.close()

def update_likes(media_id):
    conn = sqlite3.connect('media.db')
    c = conn.cursor()
    c.execute("UPDATE media SET likes = likes + 1 WHERE id = ?", (media_id,))
    conn.commit()
    conn.close()

def delete_media(media_id):
    conn = sqlite3.connect('media.db')
    c = conn.cursor()
    c.execute("DELETE FROM media WHERE id = ?", (media_id,))
    conn.commit()
    conn.close()

def get_random_media():
    conn = sqlite3.connect('media.db')
    c = conn.cursor()
    c.execute("SELECT id, name, path FROM media ORDER BY RANDOM() LIMIT 1")
    media = c.fetchone()
    conn.close()
    return media
2.文件扫描
复制代码
#scanner.py
import os
import time
import sqlite3
from database import init_db, add_media

def scan_directory(directory='video'):
    init_db()
    print(f"扫描目录: {directory}")
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.endswith(('.mp4', '.jpg', '.png', '.gif')):
                path = os.path.join(root, file)
                print(f"发现文件: {file} 路径: {path}")
                try:
                    result = add_media(file, path)
                    #print(f"add_media 返回: {result}")  # Debug: Print the return value
                    if result:
                        print(f"插入 {file} 到数据库")
                    else:
                        print(f"插入 {file} 到数据库失败")
                except Exception as e:
                    print(f"由于异常无法插入 {file} 到数据库: {str(e)}")

def incremental_scan(directory='video'):
    scanned_files = set()
    conn = sqlite3.connect('media.db')
    c = conn.cursor()
    c.execute("SELECT path FROM media")
    for row in c.fetchall():
        scanned_files.add(row[0])
    conn.close()

    print("开始增量扫描...")
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.endswith(('.mp4', '.jpg', '.png', '.gif')):
                path = os.path.join(root, file)
                if path not in scanned_files:
                    print(f"发现新文件: {file} 路径: {path}")
                    try:
                        result = add_media(file, path)
                        print(f"add_media 返回: {result}")  # Debug: Print the return value
                        if result:
                            print(f"插入 {file} 到数据库")
                        else:
                            print(f"插入 {file} 到数据库失败")
                    except Exception as e:
                        print(f"由于异常无法插入 {file} 到数据库: {str(e)}")
                else:
                    print(f"跳过已存在文件: {file}")

if __name__ == '__main__':
    init_db()
    while True:
        incremental_scan()
        time.sleep(3600)  # 每小时扫描一次
3.web服务
复制代码
#app.py

from flask import Flask, render_template, request, send_file, jsonify, send_from_directory
import os
import sqlite3
import random  # 导入 random 模块
from database import init_db, update_views, update_likes, delete_media, get_random_media

app = Flask(__name__)

def is_video_file(path):
    return path.lower().endswith('.mp4')

def is_image_file(path):
    return path.lower().endswith('.jpg') or path.lower().endswith('.jpeg') or path.lower().endswith('.png')

def get_random_media():
    conn = sqlite3.connect('media.db')
    c = conn.cursor()

    rand_num = random.random()  # Generate a random number between 0 and 1
    if rand_num < 0.9:
        c.execute("SELECT id, name, path FROM media WHERE path LIKE '%.mp4' ORDER BY RANDOM() LIMIT 1;")
    else:
        c.execute("SELECT id, name, path FROM media WHERE path LIKE '%.jpg' OR path LIKE '%.jpeg' OR path LIKE '%.png' ORDER BY RANDOM() LIMIT 1;")

    media = c.fetchone()

    conn.close()
    return media

@app.route('/')
def index():
    media = get_random_media()
    if media:
        media_id, name, path = media
        update_views(media_id)
        conn = sqlite3.connect('media.db')
        c = conn.cursor()
        c.execute("SELECT likes FROM media WHERE id = ?", (media_id,))
        likes = c.fetchone()[0]
        conn.close()
        return render_template('index.html', media_id=media_id, name=name, path=path, likes=likes,
                               is_video=is_video_file(path), is_image=is_image_file(path))
    return "No media found"

@app.route('/media/<path:filename>')
def media(filename):
    return send_from_directory(directory=os.path.dirname(filename), path=os.path.basename(filename))

@app.route('/like/<int:media_id>', methods=['POST'])
def like(media_id):
    update_likes(media_id)
    conn = sqlite3.connect('media.db')
    c = conn.cursor()
    c.execute("SELECT likes FROM media WHERE id = ?", (media_id,))
    likes = c.fetchone()[0]
    conn.close()
    return jsonify(success=True, likes=likes)

@app.route('/delete/<int:media_id>', methods=['POST'])
def delete(media_id):
    conn = sqlite3.connect('media.db')
    c = conn.cursor()
    c.execute("SELECT path FROM media WHERE id = ?", (media_id,))
    path = c.fetchone()[0]
    conn.close()
    if os.path.exists(path):
        os.remove(path)
    delete_media(media_id)
    return jsonify(success=True)

@app.route('/download/<int:media_id>')
def download(media_id):
    conn = sqlite3.connect('media.db')
    c = conn.cursor()
    c.execute("SELECT path FROM media WHERE id = ?", (media_id,))
    path = c.fetchone()[0]
    conn.close()
    return send_file(path, as_attachment=True)

if __name__ == '__main__':
    init_db()  # 确保数据库和表被初始化
    app.run(host='0.0.0.0', port=3000, debug=True)
4.启动器
复制代码
import os
import threading
import time

# 定义运行scanner.py的函数
def run_scanner():
    # 清空并创建scanner.log文件
    with open('log/scanner.log', 'wb') as f:
        pass
    os.system('python scanner.py > log/scanner.log 2>&1')

# 定义运行app.py的函数
def run_app():
    # 清空并创建app.log文件
    with open('log/app.log', 'wb') as f:
        pass
    os.system('python app.py > log/app.log 2>&1')

if __name__ == '__main__':
    # 创建log子目录
    os.makedirs('log', exist_ok=True)

    # 创建并启动线程运行scanner.py
    scanner_thread = threading.Thread(target=run_scanner)
    scanner_thread.start()

    # 等待3秒钟
    time.sleep(3)

    # 创建并启动线程运行app.py
    app_thread = threading.Thread(target=run_app)
    app_thread.start()
相关推荐
㳺三才人子6 小时前
初探 Flask
后端·python·flask·html
小技与小术7 小时前
玩转Flask
开发语言·python·flask
知识分享小能手8 小时前
Flask入门学习教程,从入门到精通,Flask智能租房——前期准备 知识点详解(5)
python·学习·flask
小鱼仙官9 小时前
Windonws 视频存储,10s/不限时
开发语言·qt·音视频
奔跑的Ma~10 小时前
Azure OpenAI Codex 详细配置与使用教程(国内用户专属)
学习·microsoft·flask·ai编程·azure
福老板的生意经11 小时前
AI 短视频全链路创作分发系统架构解析:模块化设计与核心技术实现
人工智能·系统架构·音视频
hz5678911 小时前
2026应急指挥场景视频会议系统架构设计与私有化部署实践
系统架构·音视频·实时音视频·信息与通信·视频编解码
Hommy8811 小时前
【剪映小助手】音频处理工具接口
aigc·音视频·剪映小助手·视频剪辑自动化
alphageek811 小时前
JeffMony开源的VideoDownloader,Android平台视频下载SDK
android·其他·开源·音视频
kyle-fang12 小时前
Decord详解
音视频·视频解析