灵虚御风
醉饮千觞不知愁,忘川来生空余恨!

导航

 

优酷架构图

优酷流程图

 

youku_server

服务端:
    1.conf: 配置文件
        - settings

    2.db: 模型表类
        - models

    3.download_files: 客户端上传的文件存放目录

    4.interface: 接口层
        - common_interface
        - admin_interface
        - user_interface

    5.lib: 公共方法
        - common

    6.orm: 操作数据库

    7.tcp_server: 套接字服务端
        - socket_server

    8.start:启动文件

项目需求
'''
管理员:
    1.注册
    2.登录
    3.上传视频
    4.删除视频
    5.发布公告

用户:
    1.注册
    2.登录
    3.充会员
    4.查看视频
    5.下载免费视频
    6.下载会员视频
    7.查看观影记录
'''

数据库表的设计:

youku_demo

    User:
        id, name, pwd, is_vip, is_locked, user_type, register_time

    Movie:
        id, name, path, is_free, file_md5, user_id, is_delete, upload_time

    Notice:
        id, title, content, user_id, create_time

    DownloadRecord:
        id, user_id, movie_id, download_time
redme
import os
import sys
sys.path.append(
    os.path.dirname(__file__)
)
from tcp_server.socket_server import SocketServer

if __name__ == '__main__':
    server = SocketServer() # __init
    server.run() #
start.py
import socket
import struct
import json
from interface import common_interface
from interface import admin_interface
from interface import user_interface
from concurrent.futures import ThreadPoolExecutor
from threading import Lock
from lib import lock_file
from db import user_data

lock = Lock()
lock_file.mutex = lock

func_dic = {

    'register': common_interface.register_interface,
    'login': common_interface.login_interface,
    # 查看电影是否存在接口
    'check_movie': admin_interface.check_movie_interface,

    'upload_movie': admin_interface.upload_movie_interface,
    # 获取电影列表接口
    'get_movie_list': common_interface.get_movie_list_interface,
    'delete_movie': admin_interface.delete_movie_interface,
    'put_notice': admin_interface.put_notice_interface,

    # 普通用户功能
    'buy_vip': user_interface.buy_vip_interface,
    'download_movie': user_interface.download_movie_interface,
    'check_download_record': user_interface.check_download_record_interface,
    'check_all_notice': user_interface.check_all_notice_interface,
}
class SocketServer:
    def __init__(self):
        self.server = socket.socket()
        self.server.bind(
            ('127.0.0.1',9527)
        )
        self.server.listen(5)
        self.pool = ThreadPoolExecutor(50)
    def run(self):
        print('启动服务端')
        while True:
            conn,addr = self.server.accept()
            # 通过self.pool 线程池异步提交任务(working)
            # self.pool.submit(函数对象, 传递给函数对象的参数1, 参数2)  # 1 2 3
            self.pool.submit(self.working,conn,addr)
    # 任务分发:
    def dispatcher(self,client_back_dic,conn):
        # # 判断功能的类型
        # if client_back_dic.get('type') == 'register':
        #     common_interface.register_interface(client_back_dic, conn)
        #
        # elif client_back_dic.get('type') == 'login':
        #     common_interface.login_interface(client_back_dic, conn)
        # 通过_type变量接受客户端选择的功能类型
        _type = client_back_dic.get('type') # login
        if _type in func_dic: # register
            func_dic.get(_type)(
                client_back_dic,conn
            )
    # 用户执行客户端连接任务
    def working(self,conn,addr):
        while True:
            try:
                # 每一个客户端访问服务端都会经过此处
                # 此处用于接收客户端传入的数据
                headers = conn.recv(4)
                data_len = struct.unpack('i',headers)[0]
                data_bytes = conn.recv(data_len)
                client_back_dic = json.loads(data_bytes.decode('utf-8'))
                # 把客户端传递过来的字典与conn传给dispatcher执行
                self.dispatcher(client_back_dic,conn)
            except Exception as e:
                print(e)
                lock.acquire()
                user_data.user_online.pop(str(addr))
                lock.release()
                conn.close()
                break

"""
作者:知乎用户
链接:https://www.zhihu.com/question/19786827/answer/28752144
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

. 由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户,这个机制就是Session.典型的场景比如购物车,当你点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建了特定的Session,用用于标识这个用户,并且跟踪用户,这样才知道购物车里面有几本书。这个Session是保存在服务端的,有一个唯一标识。在服务端保存Session的方法很多,内存、数据库、文件都有。集群的时候也要考虑Session的转移,在大型的网站,一般会有专门的Session服务器集群,用来保存用户会话,这个时候 Session 信息都是放在内存的,使用一些缓存服务比如Memcached之类的来放 Session。2. 思考一下服务端如何识别特定的客户?这个时候Cookie就登场了。每次HTTP请求的时候,客户端都会发送相应的Cookie信息到服务端。实际上大多数的应用都是用 Cookie 来实现Session跟踪的,第一次创建Session的时候,服务端会在HTTP协议中告诉客户端,需要在 Cookie 里面记录一个Session ID,以后每次请求把这个会话ID发送到服务器,我就知道你是谁了。有人问,如果客户端的浏览器禁用了 Cookie 怎么办?一般这种情况下,会使用一种叫做URL重写的技术来进行会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如 sid=xxxxx 这样的参数,服务端据此来识别用户。3. Cookie其实还可以用在一些方便用户的场景下,设想你某次登陆过一个网站,下次登录的时候不想再次输入账号了,怎么办?这个信息可以写到Cookie里面,访问网站的时候,网站页面的脚本可以读取这个信息,就自动帮你把用户名给填了,能够方便一下用户。这也是Cookie名称的由来,给用户的一点甜头。所以,总结一下:Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。
"""
tcp_server/socket_server.py
from DBUtils.PooledDB import PooledDB
import pymysql

# pip3 install DBUtils
POOL = PooledDB(
    creator=pymysql,# 使用链接数据库的模块
    maxconnections=6, # # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
    maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
    maxshared=3,
    # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
    blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
    maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制
    setsession=[],  # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
    ping=0,
    # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
    host='127.0.0.1',
    port=3306,
    user='root',
    password='llx20190411',
    database='youku_two',
    charset='utf8',
    autocommit='True'
)
orm/db_pool.py
import pymysql

from orm.db_pool import POOL

class Mysql:

    def __init__(self):
        # 建立连接
        self.conn = POOL.connection()
        # 获取游标
        self.cursor = self.conn.cursor(
            pymysql.cursors.DictCursor
        )
    # 关闭游标\链接方法
    def close_db(self):
        self.cursor.close()
        self.conn.close()

    # 查看
    def my_select(self,sql,args=None):
        self.cursor.execute(sql,args)
        res = self.cursor.fetchall()
        # [{}, {}, {}]
        # print(res)
        return res
    # 提交
    def my_execute(self,sql,args):
        try:
            # 把insert , update...一系列sql提交到mysql中
            self.cursor.execute(sql,args)

        except Exception as e:
            print(e)
orm/mysql_control.py
'''
定义字段类
'''
from orm.mysql_control import Mysql

# 字段
class Field:
    def __init__(self,name, column_type, primary_key, default):
        self.name = name
        self.column_type = column_type
        self.primary_key = primary_key
        self.default = default

# varchar
class StringField(Field):
    def __init__(self,name,column_type='varchar(255)',primary_key=False,default=None):
        super().__init__(name, column_type, primary_key, default)


# int
class IntegerField(Field):
    def __init__(self, name, column_type='int', primary_key=False, default=0):
        super().__init__(name, column_type, primary_key, default)



# 元类控制表模型类的创建
class OrmMetaClass(type):


    # 类名, 类的基类, 类的名称空间
    def __new__(cls,class_name,class_base,class_attr):
        # print(class_name, class_bases, class_attr)
        # 1.过滤Models类
        if class_name == 'Models':
            return type.__new__(cls,class_name,class_base,class_attr)

        # 2.控制模型表中: 表名, 主键, 表的字段
        # 如果模型表类中没有定义table_name,把类名当做表名
        # 获取表名
        table_name = class_attr.get('table_name',class_name) # # user_info, User

        # 3.判断是否只有一个主键
        primary_key = None
        # 用来存放所有的表字段, 存不是目的,目的是为了取值方便
        mappings = {}
        """
        __main__: xxxx
        'id': <__main__.IntegerField object at 0x000001E067D48B00>, 
        'name': <__main__.StringField object at 0x000001E067D48AC8>}
        
        """
        for key,value in class_attr.items():
            # 判断value是否是字段类的对象
            if isinstance(value,Field):
                # 把所有字段都添加到mappings中
                mappings[key] = value
                if value.primary_key:
                    if primary_key:
                        raise TypeError('主键只能有一个')
                    # 获取主键
                    primary_key = value.name
        # 删除class_attr中与mappings重复的属性, 节省资源
        for key in mappings.keys():
            class_attr.pop(key)
        # 判断是否有主键
        if not primary_key:
            raise TypeError('必须有一个主键')
        class_attr['table_name'] = table_name
        class_attr['primary_key'] = primary_key
        class_attr['mappings'] = mappings
        '''
               'table_name': table_name
               'primary_key': primary_key
               'mappings': {'id': <__main__.IntegerField object at 0x000001E067D48B00>,
                            'name': <__main__.StringField object at 0x000001E067D48AC8>}
                            }
               '''
        return type.__new__(cls,class_name,class_base,class_attr)


# 继承字典类
class Models(dict,metaclass=OrmMetaClass):
    def __init__(self,**kwargs):
        # print(**kwargs)  # 接收关键字参数
        super().__init__(**kwargs)
    # 在对象.属性没有的时候触发
    def __getattr__(self, item):
        # print(item)
        return self.get(item,'没有这个key')
    # 在对象.属性 = 属性值 时触发
    def __setattr__(self, key, value):
        # 字典赋值操作
        self[key] = value
    #
    @classmethod
    def select(cls,**kwargs):
        # 获取数据库链接对象
        ms = Mysql()

        # 若没有kwargs代表没有条件查询
        if not kwargs:
            # select * from table;
            sql = 'select * from %s' % cls.table_name
            res = ms.my_select(sql)
        # 若有kwargs代表有条件
        else:
            # print(kwargs)  # {id:1}
            key = list(kwargs.keys())[0] # id
            value = kwargs.get(key) # 1
            # select * from table where id=1;
            sql = 'select * from %s where %s=?' % (cls.table_name,key)
            sql = sql.replace('?','%s')
            res = ms.my_select(sql,value)

        if res:
            # [{},{}, {}]   ---->  [obj1, obj2, obj3]
            # 把mysql返回来的 列表套 字典 ---> 列表套 对象
            # l1 = []
            # # 遍历mysql返回所有的字典
            # for d in res:
            #     # 把每一个字典传给cls实例化成一个个的r1对象
            #     r1 = cls(**d)
            #     # 追加到l1列表中
            #     l1.append(r1)

            return [cls(**result) for result in res]

    # 插入
    def save(self):
        ms = Mysql()
        # insert into table(x,x,x) values(x,x,x);
        # 字段名
        fields = []
        # 字段的值
        values = []
        # 存放对应字段的 ? 号
        args = []
        
        for k,v in self.mappings.items():
            # 把主键过滤掉
            if not v.primary_key:
                fields.append(v.name)
                values.append(getattr(self,v.name,v.default))
                args.append('?')
        # insert into table(x,x,x) values(?, ?, ?);
        sql = 'insert into %s(%s) values(%s)' % (self.table_name,','.join(fields),','.join(args))
        sql = sql.replace('?', '%s')

        ms.my_execute(sql, values)
        
    # 更新
    def sql_update(self):
        ms = Mysql()

        fields = []
        primary_key = None
        values = []
        for k, v in self.mappings.items():
            # 获取主键的值
            if v.primary_key:
                primary_key = getattr(self, v.name, v.default)
            else:

                # 获取 字段名=?, 字段名=?,字段名=?
                fields.append(
                    v.name + '=?'
                )
                # 获取所有字段的值
                values.append(
                    getattr(self, v.name, v.default)
                )

        # update table set %s=?,... where id=1;  把主键当做where条件
        sql = 'update %s set %s where %s=%s' % (self.table_name, ','.join(fields), self.primary_key, primary_key)

        # print(sql)  # update User set name=? where id=3

        sql = sql.replace('?', '%s')

        ms.my_execute(sql, values)


# User, Movie, Notice
# 表模型类
class User(Models):
    # table_name = 'user_info'
    id = IntegerField(name='id',primary_key=True)
    name = StringField(name='name')
    # pwd = StringField(name='pwd')

class Movie(Models):
    d = IntegerField(name='id', primary_key=True)
    pass


# # # User('出入任意个数的关键字参数')
# user_obj = User()  # user_obj--->dict
# user_obj.name = 'xxxx'

# if __name__ == '__main__':
#     res = User.select(name='jason_sb')[0]
#     print(res)
#
#     # res.name = 'jason_sb'
#     #
#     # res.sql_update()
#
#     # user_obj = User(name='egon')
#     # user_obj.save()
#
# '''
# 表:
#     表名, 只有一个唯一的主键, 字段(必须是Field的字段)
#
# 元类:
#     通过元类控制类的创建.
# '''
#
# # class Movie:
# #     def __init__(self, movie_name, movie_type):
# #         self.movie_name = movie_name
# #         self.movie_type = movie_type
# #
# #
# # class Notice:
# #     def __init__(self, title, content):
# #         self.title = title
# #         self.content = content
#
# '''
# 问题1: 所有表类都要写__init__, 继承一个父类
# 问题2: 可以接收任意个数以及任意名字的关键字参数. 继承python中的字典对象.
# '''
#
# # if __name__ == '__main__':
# #     # d1 = dict({'name': 'tank'})
# #     # d2 = dict(name='tank2')
# #     # print(d1)
# #     # print(d2)
# #
# #     d3 = Models(name='jason')
# #     # print(d3)
# #     # print(d3.get('name'))
# #     # print(d3['name'])
# #     # print(d3.name)
# #     # d3.name = 'tank'
# #     # d3.pwd = '123'
# #     # print(d3.name)
# #     # print(d3)
# #     print(d3.name)  # None
# #
# #     d3.pwd = '123'
# #     print(d3.pwd)
# #     print(d3)



if __name__ == '__main__':
    # 查看所有
    # res = User.select()
    # print(res)

    # 根据查询条件查询
    res = User.select(name='json_egon_sb')
    print(res)
#
#     # 更新
#     # user_obj = res[0]
#     # user_obj.name = 'jason_sb_sb'
#     # user_obj.sql_update()  # {'id': 3, 'name': 'jason_sb'}
#
#     # 插入
#     # user_obj = User(name='json_egon_sb')
# #     # user_obj.save()
orm/orm.py
import json
import struct
from functools import wraps
from db import user_data
import time
import uuid
import hashlib

def send_data(send_dic,conn,file=None):
    data_bytes = json.dumps(send_dic).encode('utf-8')
    headers = struct.pack('i',len(data_bytes))
    conn.send(headers)
    conn.send(data_bytes)
    if file:
        with open(file,'tb')as f:
            for line in f:
                conn.send(line)


def get_time():
    now_time = time.strftime('%Y-%m-%d %X')
    return now_time

def get_md5_pwd(pwd):
    md5 = hashlib.md5()
    md5.update(pwd.encode('utf-8'))
    md5.update('天下第一'.encode('utf-8'))
    return md5.hexdigest()

def get_random_code():
    # uuid可以产生一个世界上唯一的字符串
    md5 = hashlib.md5()
    md5.update(str(uuid.uuid4()).encode('utf-8'))
    return md5.hexdigest()

# 登录认证装饰器
def login_auth(func):
    @wraps(func) # 装饰器修复技术
    # (client_back_dic, conn) = args
    def inner(*args,**kwargs):
        # if args[0].get('session') == 服务端存放的session值:
        # # [session, user_id] = values
        addr = args[0].get('addr')
        # addr: [session, user_id]

        # [session, user_id]
        user_session = user_data.user_online.get(addr)
        if user_session:
            if args[0].get('session') == user_session[0]:
                args[0]['user_id'] = user_session[1]

        #
        # for values in user_data.user_online.values():
        #     if args[0].get('session') == values[0]:
        #         # 添加到client_back_dic
        #         args[0]['user_id'] = values[1]  # user_id

        # 判断user_id是否存在
        if args[0].get('user_id'):
            func(*args, **kwargs)
        else:
            send_dic = {'flag': False, 'msg': '未登录,请去登录!'}
            # send_data(send_dic, conn)

            send_data(send_dic, args[1])

    return inner

"""
返回的是wrapper方法的引用,也就是让test指向了wrapper方法,所以调用test.__name__, 实际上是wrapper.__name__,这样子可能会造成后面查找该方法的名字已经注释时候会得到装饰器的内嵌函数的名字和注释。
"""
lib/common.py
mutex = None
lib/lock_file.py
from db import models
import os
from conf import settings
from lib import common
@common.login_auth
def upload_movie_interface(client_bank_dic,conn):
    print('炮王来交货啦!')
    # 确保电影名称是唯一的  随机字符串 + 电影名称
    movie_name = common.get_random_code() + client_bank_dic.get('movie_name') # .mp4

    movie_size = client_bank_dic.get('file_size')
    movie_path = os.path.join(settings.DOWNLOAD_PATH,movie_name)
    # 1.接受上传的电影
    data_recv = 0
    with open(movie_path, 'wb') as f:
        while data_recv < movie_size:
            data = conn.recv(1024)
            f.write(data)
            data_recv += len(data)

    # 2.把电影数据保存到mysql中
    movie_obj = models.Movie(
        name=movie_name,
        file_md5=client_bank_dic.get('file_md5'),
        is_free=client_bank_dic.get('is_free'),
        path=movie_path,
        user_id=client_bank_dic.get('user_id'),
        upload_time=common.get_time()
    )
    movie_obj.save()
    send_dic = {
        'flag': True, 'msg': f'{client_bank_dic.get("movie_name")}电影上传成功!'
    }

    common.send_data(send_dic, conn)

    pass
@common.login_auth
def check_movie_interface(client_back_dic,conn):
    file_md5 = client_back_dic.get('file_md5')
    movie_list = models.Movie.select(file_md5=file_md5)
    if movie_list:
        send_dic = {
            'flag': False, 'msg': '电影已存在!'
        }
    else:
        send_dic = {
            'flag': True, 'msg': '电影可以上传!'
        }
    common.send_data(send_dic,conn)

@common.login_auth
def delete_movie_interface(client_back_dic, conn):
    movie_id = client_back_dic.get('movie_id')
    # 直接删除
    movie_obj = models.Movie.select(id=movie_id)[0]
    # 调用更新方法
    movie_obj.sql_update()
    send_dic = {
        'flag': True, 'msg': '电影删除成功!'
    }
    common.send_data(send_dic, conn)

@common.login_auth
def put_notice_interface(client_back_dic, conn):
    title = client_back_dic.get('title')
    content = client_back_dic.get('content')
    user_id = client_back_dic.get('user_id')
    notice_obj = models.Notice(title=title,content=content,user_id=user_id,create_time=common.get_time())
    notice_obj.save()
    send_dic = {
        'msg': '公告发布成功!'
    }
    common.send_data(send_dic,conn)
interface/admin_interface.py
from db import models
from lib import common, lock_file
from db import user_data

def register_interface(client_back_dic, conn):
    # 写业务逻辑
    # 1.判断用户名是否存在
    username = client_back_dic.get('username')
    # 通过用户名当作条件查询
    user_obj_list = models.User.select(name=username)

    # 若存在,给客户端返回数据, 告诉用户,用户已存在!
    if user_obj_list:
        send_dic = {
            'flag':False,
            'msg':'用户已经存在'
        }
        # 若不存在,保存数据到MySQL数据库中, 返回注册成功给客户端
    else:
        password = client_back_dic.get('password')
        user_obj = models.User(
            name = username,
            #  pwd, is_vip, is_locked, user_type, register_time
            password = common.get_md5_pwd(password),
            is_vip=0 ,# 0表示不是VIP, 1表示VIP
            is_locked=0,  # 0表示不锁定, 1表示锁定
            user_type=client_back_dic.get('user_type'),
            register_time=common.get_time()
        )
        user_obj.save()
        send_dic = {'flag': True, 'msg': '注册成功'}
    common.send_data(send_dic, conn)


def login_interface(client_back_dic, conn):
    # 执行login业务逻辑
    username = client_back_dic.get('username')

    # 1.根据用户名查询用户数据
    user_list = models.User.select(name=username)

    if not user_list:
        send_dic = {'flag': False, 'msg': '用户不存在'}

    else:
        user_obj = user_list[0]
        password = client_back_dic.get('password')
        # 1.判断客户端传入的密码与数据库中的密码是否相等
        if user_obj.password == common.get_md5_pwd(password):

            # 产生一个随机字符串,作为session值
            session = common.get_random_code()
            addr = client_back_dic.get('addr')
            # 保存session值到服务端,session + user_id一同保存到服务端本地
            # 使用锁写入数据
            lock_file.mutex.acquire()
            #  str(addr)
            user_data.user_online[addr] = [session,user_obj.id]
            lock_file.mutex.release()
            send_dic = {'flag': True, 'msg': '登录成功!',
                        'session': session, 'is_vip': user_obj.is_vip, 'new_notice': None}
            new_notice = get_new_notice_interface()
            if not new_notice:
                pass
            else:
                send_dic['new_notice'] = new_notice
        else:
            send_dic = {
                'flag':False,
                'msg':'密码错误'
            }
    common.send_data(send_dic,conn)
# 获取电影接口
@common.login_auth
def get_movie_list_interface(client_back_dic, conn):
    # 获取所有电影对象
    movie_obj_list = models.Movie.select()
    # 添加未删除的电影列表
    back_movie_list = []
    if movie_obj_list:
        # 过滤已删除的电影
        for movie_obj in movie_obj_list:
            # 没有删除则返回
            if not movie_obj.is_delete: # 1代表删除
                if client_back_dic.get('movie_type') == 'all':
                    back_movie_list.append(
                        # [电影名称、是否免费、电影ID]
                        [movie_obj.name,'免费' if movie_obj.is_free else '收费',movie_obj.id]

                    )
                elif client_back_dic.get('movie_type') == 'free':
                    # 判断哪些电影是免费的
                    if movie_obj.is_free:
                        back_movie_list.append(
                            [movie_obj.name, '免费', movie_obj.id]
                        )
                else:
                    if not movie_obj.is_free:
                        back_movie_list.append(
                            [movie_obj.name, '收费', movie_obj.id]
                        )
        if back_movie_list:
            send_dic = {
                'flag':True,
                'back_movie_list':back_movie_list
            }
        else:
            send_dic = {'flag': False, 'msg': '没有电影!'}
    else:

        send_dic = {'flag': False, 'msg': '没有电影!'}
    common.send_data(send_dic,conn)
def get_new_notice_interface():
    # 1.获取所有的公告
    notice_obj_list = models.Notice.select() # 展示此处notice_obj_list的数据
    if not notice_obj_list:
        return False
    # 2.对公告的发布时间或者id进行排序,获取最新的一条公告
    notice_desc_list = sorted(
        # [notice_obj, notice_obj,notice_obj。。。]
        # 选择1:根据ID notice_obj_list, key=lambda notice_obj: notice_obj.id
        # 选择2:根据时间
        notice_obj_list,key=lambda notice_obj:notice_obj.create_time,reverse=True
    )
    print()
    new_notice = {
        'title': notice_desc_list[0].title,
        'content': notice_desc_list[0].content,
    }
    return new_notice
interface/common_interface.py
from db import models
from lib import common
import os
from conf import settings

@common.login_auth
def buy_vip_interface(client_back_dic, conn):
    user_id = client_back_dic.get('user_id')
    user_obj = models.User.select(id=user_id)[0]
    user_obj.is_vip = 1
    user_obj.sql_update()
    send_dic = {
        'flag':True,
        'msg':'会员充值成功!'
    }
    common.send_data(send_dic, conn)

@common.login_auth
def download_movie_interface(client_back_dic, conn):

    movie_id = client_back_dic.get('movie_id')
    movie_name = client_back_dic.get('movie_name')
    movie_type = client_back_dic.get('movie_type')
    user_id = client_back_dic.get('user_id')
    # movie_obj = models.Movie.select(id=movie_id)[0]
    # movie_path = movie_obj.path
    movie_path = os.path.join(settings.DOWNLOAD_PATH,movie_name)
    movie_size = os.path.getsize(movie_path)
    send_dic = {
        'flag': True, 'msg': '准备下载','movie_size': movie_size
    }
    user_obj = models.User.select(id=user_id)[0]
    if movie_type == '免费':
        wait_time = 0
        if not user_obj.is_vip:
            wait_time = 20
        send_dic['wait_time'] = wait_time
    obj = models.DownloadRecord(user_id=user_id, movie_id=movie_id,
                                download_time=common.get_time())
    obj.save()
    print(send_dic)
    common.send_data(send_dic, conn, movie_path)




@common.login_auth
def check_download_record_interface(client_back_dic, conn):
    record_obj_list = models.DownloadRecord.select()
    user_id = client_back_dic.get('user_id')

    back_record_list = []

    if record_obj_list:
        for record_obj in record_obj_list:
            if record_obj.user_id == user_id:

                # 获取当前用户下载电影记录的电影对象
                movie_obj = models.Movie.select(id=record_obj.movie_id)[0]

                back_record_list.append(
                    movie_obj.name
                )

            else:
                send_dic= {'flag': False, 'msg': '当前用户没有下载记录!'}

        send_dic = {
            'flag':True, 'record_list': back_record_list
        }

    else:
        send_dic = {'flag': False, 'msg': '没有任何下载记录!'}

    common.send_data(send_dic, conn)


@common.login_auth
def check_all_notice_interface(client_back_dic, conn):

    notice_obj_list = models.Notice.select()

    back_notice_list = []

    if notice_obj_list:

        for notice_obj in notice_obj_list:
            title = notice_obj.title
            content = notice_obj.content
            back_notice_list.append(
                {'title': title, 'content': content}
            )

        send_dic = {'flag': True, 'back_notice_list': back_notice_list}

    else:
        send_dic = {'flag': False, 'msg': '没有公告!'}

    common.send_data(send_dic, conn)
intelface/user_interface.py
user_online = {
    # addr:[session,user_id]
}
db/user_data.py
from orm.orm import Models,StringField,IntegerField

# 用户表
class User(Models):
    # 表名
    table_name = 'userinfo'
    # 字段
    id = IntegerField(name='id',primary_key=True)
    name = StringField(name='name')
    #  pwd, is_vip, is_locked, user_type, register_time
    pwd = StringField(name='password')
    is_vip = IntegerField(name='is_vip')
    is_locked = IntegerField(name='is_locked')
    user_type = StringField(name='user_type')
    register_time = StringField(name='register_time')
    
# 电影表
class Movie(Models):
    # 表名
    table_name = 'movie'
    # 字段
    id = IntegerField(name='id', primary_key=True)
    name = StringField(name='name')
    # path, is_free, file_md5, user_id, is_delete, upload_time
    path = StringField(name='path')
    is_free = IntegerField(name='is_free')  # 1   0
    file_md5 = StringField(name='file_md5')
    user_id = IntegerField(name='user_id')
    is_delete = IntegerField(name='is_delete')
    upload_time = StringField(name='upload_time')

# 公告表
class Notice(Models):
    table_name = 'notice'
    # 字段
    id = IntegerField(name='id', primary_key=True)
    title = StringField(name='title')
    content = StringField(name='content')
    user_id = IntegerField(name='user_id')
    create_time = StringField(name='create_time')


# 下载记录表
class DownloadRecord(Models):
    table_name = 'download_record'
    # 字段
    id = IntegerField(name='id', primary_key=True)
    user_id = IntegerField(name='user_id')
    movie_id = IntegerField(name='movie_id')
    download_time = StringField(name='download_time')
db/models.py
import os

BASE_PATH = os.path.dirname(os.path.dirname(__file__))

IP = '127.0.0.1'

PORT = 9528

coding = 'utf-8'


HOMES = os.path.join(BASE_PATH,'homes')
DB_PATH = os.path.join(BASE_PATH,'db')
conf/settings.py

 

youku_client

客户端:
    1.conf: 配置文件
        - settings

    2.core: 核心代码
        - src
        - admin 管理员视图
        - user 用户视图

    3.download_files: 下载文件目录

    4.lib: 公共方法
        - common

    5.tcp_client: 套接字客户端
        socket_client

    6.upload_files: 上传文件目录

    7.start: 启动文件
readme
import os
import sys
from core import src
sys.path.append(os.path.dirname(__file__))

if __name__ == '__main__':
    src.run()
start.py
import socket

class SocketClient:
    def __init__(self):
        self.client = socket.socket()
        self.client.connect(
            ('127.0.0.1',9527)
        )
    def get_client(self):
        return self.client

# sk = SocketClient()
# client = sk.get_client()
tcp_client/socket_client.py
import json
import struct
import os
from conf import settings
import hashlib

def send_msg_back_dic(send_dic,client,file=None):
    data_bytes = json.dumps(send_dic).encode('utf-8')
    headers = struct.pack('i',len(data_bytes))
    client.send(headers)
    client.send(data_bytes)
    # 上传电影
    if file:
        with open(file,'rb')as f:
            for line in f:
                # print(line)
                client.send(line)
    headers = client.recv(4)
    data_len = struct.unpack('i',headers)[0]
    data_bytes = client.recv(data_len)
    back_dic = json.loads(data_bytes.decode('utf-8'))
    print(1111111)
    return back_dic

def get_movie_list():
    if os.path.exists(settings.UPLOAD_FILES):
        movie_list = os.listdir(settings.UPLOAD_FILES)
        if movie_list:
            return movie_list

# 获取电影的md5值
def get_movie_md5(movie_path):
    md5 = hashlib.md5()
    # 截取电影的四个位置 的 md5值
    movie_size = os.path.getsize(movie_path)
    # 从电影的4个位置个截取10个bytes数据
    current_index = [
        0,movie_size // 3,(movie_size // 3) * 2,
        movie_size - 10
    ]
    with open(movie_path,'rb') as f:
        for index in current_index:

            f.seek(index)
            data = f.read(10)
            md5.update(data)
    return md5.hexdigest()
"""
file.seek()方法标准格式是:seek(offset,whence=0)offset:开始的偏移量,也就是代表需要移动偏移的字节数whence:给offset参数一个定义,表示要从哪个位置开始偏移;0代表从文件开头开始算起,1代表从当前位置开始算起,2代表从文件末尾算起。默认为0
"""
lib/common.py
2a026dfc6914f552a379847c0e88edb202 网络编程简介.mp4
7c008939a642db5065e4f497f43ad5c901 json扩展功能.mp4
46beec8f89b84753fee8790252aeb26103 OSI七层协议.mp4
47ff8eba5175b6e5e9a8b84d0fb29db904 三次握手四次挥手.mp4
download_files
from tcp_client import socket_client
from lib import common
import os
from conf import settings
user_info = {
    'cookies':None
}
def register(client):
    while True:
        username = input('请输入用户名:').strip()
        password = input('请输入密码:').strip()
        re_password = input('请确认密码:').strip()
        if password == re_password:
            send_dic = {'username': username,
                        'password': password,
                        'type': 'register',
                        'user_type': 'admin'}
            # {'flag': False, 'msg': '用户已存在!'}
            # {'flag': True, 'msg': '注册成功'}
            back_dic = common.send_msg_back_dic(send_dic, client)
            if back_dic.get('flag'):
                print(back_dic.get('msg'))
                break

            else:
                print(back_dic.get('msg'))

def login(client):
    while True:
        username = input('请输入用户名: ').strip()
        password = input('请输入密码:').strip()

        send_dic = {
            'type': 'login',  # 判断对应的接口
            'username': username,
            'password': password,
            'user_type': 'admin'
        }
        # {'flag': False, 'msg': '用户不存在'}
        back_dic = common.send_msg_back_dic(send_dic, client)

        if back_dic.get('flag'):
            session = back_dic.get('session')
            user_info['cookies'] = session
            print(back_dic.get('msg'))
            break

        else:
            print(back_dic.get('msg'))
def upload_movie(client):
    while True:
        # 1.打印电影列表
        movie_list = common.get_movie_list()
        for index,movie in enumerate(movie_list):
            print(index,movie)
        choice = input('请输入电影编号:>>>').strip()
        if not choice.isdigit():
            print('请输入数字!')
            continue
        choice = int(choice)
        if choice not in range(len(movie_list)):
            print("请选择正确编号")
            continue
        # 用户选择电影
        movie_name = movie_list[choice]
        # 上传电影绝对路径
        movie_path = os.path.join(settings.UPLOAD_FILES,movie_name)
        # 2.去服务端校验电影是否存在
        file_md5 = common.get_movie_md5(movie_path)
        send_dic = {
            'type':'check_movie',
            'session':user_info.get('cookies'),
            'file_md5':file_md5,
        }
        back_dic = common.send_msg_back_dic(send_dic,client)
        if back_dic.get('flag'):
            # 电影可以上传
            print(back_dic.get('msg'))

            # 上传电影功能字典
            send_dic = {
                'type': 'upload_movie',
                'file_md5': file_md5,
                # 大小用来判断服务端需要接受文件的大小
                'file_size':os.path.getsize(movie_path),
                'movie_name':movie_name,
                'session':user_info.get('cookies')
            }
            is_free = input('上传电影是否免费(y/n):>>>').strip()
            if is_free == 'y':
                send_dic['is_free'] = 1
            else:
                send_dic['is_free'] = 0
            back_dic = common.send_msg_back_dic(send_dic,client)
            if back_dic.get('flag'):
                print(back_dic.get('msg'))
                break

        else:
            print(back_dic.get('msg'))

    #
    #
    # send_dic = {'type': 'upload_movie','session': user_info.get('cookies')}
    # back_dic = common.send_msg_back_dic(send_dic, client)
    # print(back_dic)

    pass
def delete_movie(client):
    while True:
        # 1.从服务端获取电影列表
        send_dic = {'type': 'get_movie_list',
                    'session': user_info.get('cookies')}
        # 发送获取电影请求
        back_dic = common.send_msg_back_dic(
            send_dic, client)
        if back_dic.get('flag'):
            back_movie_list = back_dic.get('back_movie_list')
            # 打印选择的电影
            for index, movie_list in enumerate(back_movie_list):
                print(index, movie_list)
            # 2.选择需要删除的电影
            choice = input('请输入需要删除的电影编号:').strip()

            if not choice.isdigit():
                continue

            choice = int(choice)

            if choice not in range(len(back_movie_list)):
                continue

            # 获取电影ID,传递给服务端,让服务端去mysql数据库修改当前电影对象的is_delete=1
            movie_id = back_movie_list[choice][2]
            send_dic = {
                'type': 'delete_movie', 'movie_id': movie_id,
                'session': user_info.get('cookies')
            }

            # 发送删除电影请求
            back_dic = common.send_msg_back_dic(send_dic, client)
            if back_dic.get('flag'):
                print(back_dic.get('msg'))
                break
        else:
            print(back_dic.get('msg'))
            break
def put_notice(client):
    title = input('请输入公告标题:').strip()
    content = input('请输入公告内容:').strip()

    send_dic = {
        'type': 'put_notice',
        'session': user_info.get('cookies'),
        'title': title,
        'content': content
    }
    back_dic = common.send_msg_back_dic(send_dic, client)

    print(back_dic.get('msg'))

func_dic = {
    '1': register,
    '2': login,
    '3': upload_movie,
    '4': delete_movie,
    '5': put_notice,
}


def admin_view():
    sk_client = socket_client.SocketClient()
    client = sk_client.get_client()
    while True:
        print('''
            1.注册
            2.登录
            3.上传视频
            4.删除视频
            5.发布公告
            q.退出
        ''')

        choice = input('请选择功能编号:').strip()

        if choice == 'q':
            break

        if choice not in func_dic:
            continue

        func_dic[choice](client)
core/admin.py
from core import admin,user


func_dic = {
    '1': admin.admin_view,
    '2': user.user_view,
}



def run():
    while True:
        print('''
                1.管理员功能
                2.用户功能
                q.退出
                ''')
        choice = input('请输入功能编号:').strip()
        if choice == 'q':
            break
        if choice not in func_dic:
            continue
        func_dic[choice]()
core/src.py
import time
from tcp_client.socket_client import SocketClient
from lib import common
import os
from conf import settings
user_info = {
    'cookies': None,
    'is_vip': None
}
def register(client):
    while True:
        username = input('请输入用户名:>>>').strip()
        password = input('请输入密码:').strip()
        re_password = input('请确认密码:').strip()
        if password == re_password:
            send_dic = {'username': username,
                        'password': password,
                        'type': 'register',
                        'user_type': 'user'}
            # {'flag': False, 'msg': '用户已存在!'}
            # {'flag': True, 'msg': '注册成功'}
            back_dic = common.send_msg_back_dic(send_dic,client)
            if back_dic.get('flag'):
                print(back_dic.get('msg'))
                break
            else:
                print(back_dic.get('msg'))

def login(client):
    while True:
        username = input('请输入用户名: ').strip()
        password = input('请输入密码:').strip()

        send_dic = {
            'type': 'login',  # 判断对应的接口
            'username': username,
            'password': password,
            'user_type': 'user'
        }
        # {'flag': False, 'msg': '用户不存在'}
        back_dic = common.send_msg_back_dic(send_dic, client)

        if back_dic.get('flag'):
            session = back_dic.get('session')
            user_info['cookies'] = session
            user_info['is_vip'] = back_dic.get('is_vip')
            print(back_dic.get('msg'))

            # 打印最新公告
            if back_dic.get('new_notice'):
                print(back_dic.get('new_notice'))

            break

        else:
            print(back_dic.get('msg'))

def buy_vip(client):
    if user_info.get('is_vip'):
        print('已经是会员')
        return
    is_vip = input('购买会员(y or n)?:>>>').strip()
    if is_vip == 'y':
        send_dic = {
            'type': 'buy_vip',
            'session': user_info.get('cookies')
        }
        back_dic = common.send_msg_back_dic(
            send_dic, client)

        if back_dic.get('flag'):
            print(back_dic.get('msg'))

    else:
        print('*穷*,快去打工赚钱!')

# 查看所有电影
def check_all_movie(client):
    send_dic = {
        'type':'get_movie_list',
        'session':user_info.get('cookies')
    }
    back_dic = common.send_msg_back_dic(send_dic,client)
    if back_dic.get('flag'):
        print(back_dic.get('back_movie_list'))
    else:
        print(back_dic.get('msg'))

    pass
def download_free_movie(client):
    while True:
        # 1.获取服务端 所有免费电影
        send_dic = {
            'type':'get_movie_list',
            'session':user_info.get('cookies'),
            'movie_type':'free'
        }
        back_dic = common.send_msg_back_dic(send_dic,client)
        if back_dic.get('flag'):
            # 2.选择下载的免费电影,并提交给服务端
            movie_list = back_dic.get('back_movie_list')
            for index,movie in enumerate(len(movie_list)):
                print(index,movie)
            choice = input('请输入下载电影编号:>>>').strip()
            if not choice.isdigit():
                continue
            choice = int(choice)
            if choice not in range(len(movie_list)):
                continue
            movie_name,movie_type,movie_id = movie_list[choice]
            send_dic = {
                'type': 'download_movie',
                'session': user_info.get('cookies'),
                'movie_id': movie_id,
                'movie_name': movie_name,
                'movie_type': movie_type}

            back_dic = common.send_msg_back_dic(send_dic, client)

            if back_dic.get('flag'):
                # 3.开始下载电影
                movie_path = os.path.join(settings.DOWNLOAD_FILES,movie_name)
                movie_size = back_dic.get('movie_size')
                # 准备下载电影: 判断是否是VIP,若不是则等待广告播放
                wait_time = back_dic.get('wait_time')
                if wait_time:
                    print('惠州某工厂上线啦....')
                    time.sleep(wait_time)
                recv_data = 0
                with open(movie_path,'wb') as f:
                    while recv_data < movie_size:
                        data = client.recv(1024)
                        f.write(data)
                        recv_data += len(data)
                    f.flush()
                print('免费电影下载成功!')
                break

        else:
            print(back_dic.get('msg'))
            break
def download_pay_movie(client):
    while True:

        if user_info.get('is_vip'):
            is_pay = input('VIP打骨折,收费5$一部(y or n):').strip()
        else:
            is_pay = input('普通用户,收费50$一部(y or n):').strip()

        if not is_pay == 'y':
            print('Gun去充钱!')
            break
        # 1.获取服务端所有免费电影
        send_dic = {
            'type': 'get_movie_list',
            'session': user_info.get('cookies'),
            'movie_type': 'pay'
        }
        back_dic = common.send_msg_back_dic(send_dic, client)
        if back_dic.get('flag'):
            # 2.选择下载的免费电影,并提交给服务端
            movie_list = back_dic.get('back_movie_list')

            for index, movie in enumerate(movie_list):
                print(index, movie)

            choice = input('请输入下载电影编号:').strip()

            if not choice.isdigit():
                continue

            choice = int(choice)

            if choice not in range(len(movie_list)):
                continue

            movie_name, movie_type, movie_id = movie_list[choice]

            send_dic = {'type': 'download_movie',
                        'session': user_info.get('cookies'),
                        'movie_id': movie_id,
                        'movie_name': movie_name,
                        'movie_type': movie_type}

            back_dic = common.send_msg_back_dic(send_dic, client)

            if back_dic.get('flag'):
                # 3.开始下载电影
                movie_path = os.path.join(settings.DOWNLOAD_FILES, movie_name)
                movie_size = back_dic.get('movie_size')

                # 准备下载电影: 判断是否是VIP,若不是则等待广告播放
                wait_time = back_dic.get('wait_time')

                if wait_time:
                    time.sleep(wait_time)

                recv_data = 0
                with open(movie_path, 'wb') as f:
                    while recv_data < movie_size:
                        data = client.recv(1024)
                        f.write(data)
                        recv_data += len(data)
                    f.flush()

                print('收费电影下载成功!')
                break

        else:
            print(back_dic.get('msg'))
            break


def check_download_record(client):
    send_dic = {'type': 'check_download_record',
                'session': user_info.get('cookies')}
    back_dic = common.send_msg_back_dic(send_dic, client)

    if back_dic.get('flag'):
        # 返回电影下载记录
        print(back_dic.get('record_list'))

    else:
        print(back_dic.get('msg'))


def check_all_notice(client):
    send_dic = {'type': 'check_all_notice',
                'session': user_info.get('cookies')}
    back_dic = common.send_msg_back_dic(send_dic, client)
    if back_dic.get('flag'):
        print(back_dic.get('back_notice_list'))
    else:
        print(back_dic.get('msg'))

func_dic = {
    '1': register,
    '2': login,
    '3': buy_vip,
    '4': check_all_movie,
    '5': download_free_movie,
    '6': download_pay_movie,
    '7': check_download_record,
    '8': check_all_notice,
}
def user_view():
    sk_client = SocketClient()
    client = sk_client.get_client()
    while True:
        print('''
        1.注册
        2.登录
        3.充会员
        4.查看视频
        5.下载免费视频
        6.下载会员视频
        7.查看观影记录
        8.查看所有公告
        ''')
        choice = input('请选择功能编号:').strip()

        if choice == 'q':
            break

        if choice not in func_dic:
            continue

        func_dic[choice](client)
core/user.py
import os

BASE_PATH = os.path.dirname(os.path.dirname(__file__))

UPLOAD_FILES = os.path.join(BASE_PATH,'upload_files')

DOWNLOAD_FILE = os.path.join(BASE_PATH,'download_files')
conf/settings.py

 

posted on 2022-03-28 21:34  没有如果,只看将来  阅读(40)  评论(0编辑  收藏  举报