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

本地短视频服务器

背景:我的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()
相关推荐
Bluetangos6 小时前
视频参考帧和重构帧复用
视频编解码·视频
LNTON羚通7 小时前
视频共享融合赋能平台LnyonCVS国标视频监控平台包含哪些功能
大数据·网络·人工智能·算法·音视频
山雨楼10 小时前
ExoPlayer架构详解与源码分析(14)——ProgressiveMediaPeriod
android·架构·音视频·源码·exoplayer·media3
Maynor99612 小时前
如何利用GPT-4o生成有趣的梗图
gpt·音视频
ac-er888816 小时前
分享:Motionity-开源的Web端动画编辑器
编辑器·动画·视频·lottie·motionity·pixabay
aqi0017 小时前
FFmpeg开发笔记(三十五)Windows环境给FFmpeg集成libsrt
ffmpeg·音视频·直播·流媒体
陪你去流浪_17 小时前
Vue 使用Audio或AudioContext播放本地音频
前端·vue.js·音视频
TSINGSEE17 小时前
从“钓”到“管”:EasyCVR一体化视频解决方案助力水域安全管理
人工智能·安全·音视频·视频监控·钓鱼·安防监控系统
Likeadust17 小时前
从海上长城到数字防线:视频技术在海域边防现代化中的创新应用
人工智能·音视频·安防监控·视频监控
标贝科技17 小时前
AI视频生成技术爆发 引领虚拟数字人产业新潮流
数据库·人工智能·音视频·人机交互·交互·语音识别·虚拟现实