Django案例 - 音乐网站实现

  因为之前的云存储课程,要做一个跟云存储oss相关的案例,因为正好那会儿在学Django,所以首先就想到了用这个来实现一个网站,有想过什么第三方库啊之类的,因为之前有听说过什么七牛云,还有将静态文件压缩保存之类的,之前实验室的上司也曾经有项目涉及到用亚马逊云保存资源的案例,所以就有了我们现在的这个项目,主要是用Django实现一个音乐网站,就不涉及到数据库和模型之类的了,充其量只有用户模块会稍稍涉及一点模型,不过数据很小,占用资源也不多,之后会想其它方法来解决
  前端页面主要是用HTML+js来实现,歌词、歌单也是从js文件里面直接获取,主要就是用Django实现数据传递和动态加载

文件结构

  直接用tree生成的,没有略去无关文件,主要是让大家简单了解下所用到的模块和文件结构

卷 F 的文件夹 PATH 列表
卷序列号为 0F02-10B8
F:.
│  db.sqlite3
│  manage.py
│  settings.py
│  tree.txt
│  urls.py
│  wsgi.py
│  __init__.py
│  
├─.idea
│      dataSources.local.xml
│      dataSources.xml
│      misc.xml
│      modules.xml
│      oss_test1.iml
│      workspace.xml
│      
├─file
│  │  admin.py
│  │  apps.py
│  │  models.py
│  │  test2.py
│  │  tests.py
│  │  urls.py
│  │  views.py
│  │  __init__.py
│  │  
│  ├─migrations
│  │  │  __init__.py
│  │  │  
│  │  └─__pycache__
│  │          __init__.cpython-36.pyc
│  │          
│  ├─templatetags
│  │  │  url_tag.py
│  │  │  __init__.py
│  │  │  
│  │  └─__pycache__
│  │          url_tag.cpython-36.pyc
│  │          __init__.cpython-36.pyc
│  │          
│  └─__pycache__
│          admin.cpython-36.pyc
│          models.cpython-36.pyc
│          urls.cpython-36.pyc
│          views.cpython-36.pyc
│          __init__.cpython-36.pyc
│          
├─media
│  └─upload
│      └─2018
│          └─02
│              └─05
│                      lwpbrq.png
│                      
├─oss_test1
│  │  settings.py
│  │  urls.py
│  │  wsgi.py
│  │  __init__.py
│  │  
│  └─__pycache__
│          settings.cpython-36.pyc
│          urls.cpython-36.pyc
│          wsgi.cpython-36.pyc
│          __init__.cpython-36.pyc
│          
├─search
│  │  admin.py
│  │  apps.py
│  │  models.py
│  │  tests.py
│  │  urls.py
│  │  views.py
│  │  __init__.py
│  │  
│  ├─migrations
│  │  │  __init__.py
│  │  │  
│  │  └─__pycache__
│  │          __init__.cpython-36.pyc
│  │          
│  └─__pycache__
│          admin.cpython-36.pyc
│          models.cpython-36.pyc
│          urls.cpython-36.pyc
│          views.cpython-36.pyc
│          __init__.cpython-36.pyc
│          
├─static
│  │  base.css
│  │  home.css
│  │  jquery-1.12.4.min.js
│  │  
│  ├─bootstrap-3.3.7
│  │  ├─css
│  │  │      bootstrap.min.css
│  │  │      bootstrap.min.css.map
│  │  │      
│  │  ├─fonts
│  │  │      glyphicons-halflings-regular.eot
│  │  │      glyphicons-halflings-regular.svg
│  │  │      glyphicons-halflings-regular.ttf
│  │  │      glyphicons-halflings-regular.woff
│  │  │      glyphicons-halflings-regular.woff2
│  │  │      
│  │  └─js
│  │          bootstrap.min.js
│  │          
│  ├─cloud
│  │      黄耀明 - 暗涌(Live) - live.mp3
│  │      
│  ├─css
│  │  │  base.css
│  │  │  bootstrap.css.map
│  │  │  bootstrap.min.css
│  │  │  bootstrap.min.css.map
│  │  │  bulma.min.css
│  │  │  index.css
│  │  │  music.css
│  │  │  
│  │  └─img
│  │          1.jpg
│  │          alin.jpg
│  │          disk.png
│  │          dknz.jpg
│  │          hym.jpg
│  │          js.jpg
│  │          lf.jpg
│  │          lkq.jpg
│  │          lry.jpg
│  │          lzs.jpg
│  │          play_btn_next.png
│  │          play_btn_prev.png
│  │          play_icn_loop.png
│  │          play_icn_src.png
│  │          play_rdi_btn_pause.png
│  │          play_rdi_btn_play.png
│  │          process_btn.png
│  │          song_play_icon.png
│  │          tofirstpage.jpg
│  │          xg.jpg
│  │          xuruyun.jpg
│  │          zhl.jpg
│  │          zjx.jpg
│  │          
│  ├─fonts
│  │      glyphicons-halflings-regular.eot
│  │      glyphicons-halflings-regular.svg
│  │      glyphicons-halflings-regular.ttf
│  │      glyphicons-halflings-regular.woff
│  │      glyphicons-halflings-regular.woff2
│  │      
│  ├─img
│  │      alin.jpg
│  │      disk.png
│  │      dknz.jpg
│  │      download.png
│  │      file.png
│  │      hym.jpg
│  │      img.jpg
│  │      js.jpg
│  │      lf.jpg
│  │      lkq.jpg
│  │      lry.jpg
│  │      lzs_.jpg
│  │      play_disc.png
│  │      play_needle.png
│  │      search.png
│  │      timg.jpg
│  │      upload.png
│  │      xg.jpg
│  │      xuruyun.jpg
│  │      zhl.jpg
│  │      zjx.jpg
│  │      
│  ├─js
│  │      index.js
│  │      jquery-min.js
│  │      lyrics.js
│  │      move.js
│  │      music.js
│  │      
│  └─media
├─templates
│  │  base.html
│  │  error.html
│  │  form.html
│  │  index.html
│  │  index2.html
│  │  
│  ├─file
│  │      detail.html
│  │      list.html
│  │      search_detail.html
│  │      search_list.html
│  │      
│  └─user
│          bind_email.html
│          forgot_password.html
│          login.html
│          register.html
│          user_info.html
│          
├─user
│  │  admin.py
│  │  context_processors.py
│  │  forms.py
│  │  models.py
│  │  urls.py
│  │  views.py
│  │  __init__.py
│  │  
│  ├─migrations
│  │  │  0001_initial.py
│  │  │  0002_auto_20181121_1311.py
│  │  │  __init__.py
│  │  │  
│  │  └─__pycache__
│  │          
│  │          
│  └─__pycache__
│          
│          
└─__pycache__

主页

 页面全都是用bootstrap框架做的简单样例,基础(base.html)模板中定义了播放器主页面,上方导航栏定义的是用户模块,包括搜索、上传、下载和用户登录注册模块,如下图(开场动画我就略过了,播放器代码是在github上down的某位大佬的,简单修改了下)


image.png

  主页主要是是实现了下js数据的传递,播放器本来是从一个专门存储歌词、歌单的js文件里面读取数据,现在我将歌词和歌曲都放在了云服务器的对象存储桶(oss)里面,所以就直接查询数据将它返回到主页,js数据直传详情参考我之前的文章,这里因为我的服务器带宽等一系列条件限制,大量数据处理会反应较慢,这里就只是简单对歌单文件进行了处理,歌词和图片我并没有全部上传(数据太多爆桶要加钱呀QAQ),但是处理方法都是一样的,这里就仅用歌单做个演示,其他数据使用默认,oss的处理也可以参考我前面的内容

def index(request):
    base_url = "https://maya-music.oss-cn-beijing.aliyuncs.com/"
    files_name = []
    url_list = []
    data = []
    # 打印出云端每个文件的名字,将名字存入files_name列表
    for i in oss2.ObjectIterator(bucket):
        files_name.append(i.key[:-4])
    # 拼接出所有链接,存入url_list字典
    for name in files_name:
        url_list.append(base_url + name + '.mp3')
    for song, src in zip(files_name, url_list):
        data.append({'song': song, 'src': src, 'singer': "黄耀明", 'img': 'static/css/img/hym.jpg', 'lyric': 'anyong'})
    return render(request, 'index.html', {'list': json.dumps(data, ensure_ascii=False)})

上传功能

在导航栏内添加了bootstrap表单可以实现文件定位,然后Django获取的文件对象,利用自带的文件处理模块,将文件下载至本地,然后用oss模块处理上传至服务器,最后利用chdir、remove等方法将本地文件删除即实现了一个“假上传功能”,只是个用来熟悉Django的小练习嘛不必较真

def upload_file(request):
    referer = request.META.get('HTTP_REFERER', reverse('home'))

    if request.method == "POST":    # 请求方法为POST时,进行处理
        myFile =request.FILES.get("myfile", None)    # 获取上传的文件,如果没有文件,则默认为None
        if not myFile:
            return HttpResponse("no files for upload!")
        destination = open(os.path.join(dir_path, myFile.name), 'wb+')    # 打开特定的文件进行二进制的写操作
        for chunk in myFile.chunks():      # 分块写入文件
            destination.write(chunk)
        destination.close()
        with open(oss2.to_unicode(dir_path+"/"+myFile.name), 'rb') as f:
            bucket.put_object(myFile.name, f)
        temp_path = ".\static"
        os.chdir(os.path.abspath(temp_path))
        shutil.rmtree("media")
        os.mkdir("media")
        os.chdir(now_pwd)
        return redirect(request.GET.get('from', reverse('home')))

下载功能

原理同上,同样是下载至本地然后实现本地下载,最后删除文件,但是在处理方式上会有些不同,因为没有设计models,所以这里无法定位到具体的歌曲对象,只能实现单首歌曲下载,我的html里面的audio恰好是获取歌单对象,动态实现切换歌曲源,所以audio里面的src恰好就是当前歌曲的url,这里就钻了个空子,直接用requests+BeautufulSoup获取到audio下的src,再将数据转递过去,根据src在远端服务器进行匹配,获取到对应url后进行下载,这里oss无法处理url里面的中文字符,涉及到url转码,可以参考我之前的相应版块的介绍

def getHTMLText(url):
    try:
        r = requests.get(url, timeout=30)
        r.raise_for_status()#状态码为200则返回文本否则抛出异常
        r.encoding = 'utf-8'
        # r.encoding = r.apparent_encoding

        return r.text
    except:
        return "产生异常"

def get_data(html):
    src = BeautifulSoup(html, 'html.parser', from_encoding='utf-8').find('audio', {'id': 'audio'}).get('src')
    return src


def download_file(request):
    referer = request.META.get('HTTP_REFERER', reverse('home'))
    url = "http://127.0.0.1:8000/"
    html = getHTMLText(url)
    src = get_data(html)
    file_name = unquote(src, 'utf-8').split('com/')[1]
    temp_path2 = ".\static\cloud"
    os.chdir(os.path.abspath(temp_path2))
    print("***************")
    print("cloud:", os.getcwd())
    bucket.get_object_to_file(file_name, file_name)
    file = open(file_name, 'rb')
    response = FileResponse(file)
    response['Content-Type'] = 'application/octet-stream'
    response['Content-Disposition'] = 'attachment;filename="music.mp3"'
    # file.close()
    # os.remove(file_name)
    os.chdir(now_pwd)
    return response
           # redirect(request.GET.get('from', reverse('home')))

搜索功能

  搜索功能因为没有设计models的原因,也没有办法用自带的搜索模块了(模糊查询),这里就只能自己设计,主要是根据表单内传入的字符串去远端服务器内进行匹配,因为服务器内文件的url是根据名字生成的,会包含文件名,所以可以根据名字进行查询,然后返回结果集,将数据返回到新的展示页面,页面就简单展示了下数据,并没有特殊设计,同样继承自base.html


image.png
def get_filesname():
    files_name = []  # 歌曲名字
    for i in oss2.ObjectIterator(bucket):
        files_name.append(i.key[:-4])
    return files_name

def get_picname(list_):
    pic_name = []  # 图片全称
    for name in list_:
        pic_name.append("https://maya-picture.oss-cn-beijing.aliyuncs.com/"+name+".jpg")
    return pic_name

def get_singer(list_):
    singers = []  # 歌手名字
    for name in list_:
        s_name =  name.split(' - ')[0]
        singers.append(s_name)
    return singers

def search(request):
    search_page = request.POST.get('name')
    # search_page = input("请输入")
    search_song = []
    search_singer = []
    context = {}
    files_name = get_filesname()
    for name in files_name:
        if search_page in name:
            search_song.append(name)
    search_singer = get_singer(search_song)
    search_pic = get_picname(search_song)
    context['search_song'] = search_song
    context['search_singer'] = search_singer
    context['search_pic'] = search_pic
    context['result'] = list(zip(search_song, search_singer, search_pic))
    context['src'] = search_song
    for x,y,z in zip(context['search_song'],context['search_singer'],context['search_pic']):
        print("song:%s singer:%s pic:%s" %(x,y,z))

    return render(request, 'file/search_list.html', context)

歌曲详情页

  这里本来是除了展示歌曲详情信息,另外还想增加歌单模块、评论模块的,但是依然是苦于models的折磨,就只能暂时实现了一个无后台的纯前端评论块,没错,纯前台,使用的大佬们的第三方接口,承蒙分享,其他的基本跟上一个板块类似,数据传递,返回模板


image.png
def detail(request, src):
    ##   src  ==  歌曲名字
    song_name = []
    singer_name = []
    pic_src = []
    song_name.append(src)
    singer_name.append(src.split(' - ')[0])
    pic_src.append("https://maya-picture.oss-cn-beijing.aliyuncs.com/"+src+".jpg")
    context = {}
    context['result'] = list(zip(song_name,singer_name,pic_src))
    print("song_name:%s,singer:%s,pic:%s" %(song_name, singer_name, pic_src))
    return render(request, 'file/search_detail.html', context)

用户模块

注册和登录功能参考的都是Django自带的auth模块,结合了[邮件](# https://docs.djangoproject.com/en/2.0/topics/email/)模块,利用ajax动态加载数据,实现即时传递以及数据校验,用户模块的话是可以在admin后台看到信息的,进行添加删除,邮件设置的时候校验码和邮箱类型设置得注意,按照官网模板进行设置就好了,这一块的功能无非就是注册登录,密码修改、邮箱验证、名字修改、忘记密码,退出登录这一些,都比较简单,简单展示下基础实现的代码,更多涉及数据校验之类的详情参考源码

image.png

image.png
def login(request):
    if request.method == 'POST':
        login_form = LoginForm(request.POST)
        if login_form.is_valid():
            user = login_form.cleaned_data['user']
            auth.login(request, user)
            return redirect(request.GET.get('from', reverse('home')))
    else:
        login_form = LoginForm()

    context = {}
    context['login_form'] = login_form
    return render(request, 'user/login.html', context)

def register(request):
    if request.method == 'POST':
        reg_form = RegForm(request.POST, request=request)
        if reg_form.is_valid():
            username = reg_form.cleaned_data['username']
            email = reg_form.cleaned_data['email']
            password = reg_form.cleaned_data['password']
            # 创建用户
            user = User.objects.create_user(username, email, password)
            user.save()
            # 清除session
            del request.session['register_code']
            # 登录用户
            user = auth.authenticate(username=username, password=password)
            auth.login(request, user)
            return redirect(request.GET.get('from', reverse('home')))
    else:
        reg_form = RegForm()

    context = {}
    context['reg_form'] = reg_form
    return render(request, 'user/register.html', context)
    
def logout(request):
    auth.logout(request)
    return redirect(request.GET.get('from', reverse('home')))

def user_info(request):
    context = {}
    return render(request, 'user/user_info.html', context)

总结

  项目难点确实不大,主要是熟悉Django以及oss的工作流程,借此也熟悉了很多python的模块,诸如上传下载还有os模块,还有对字符串的处理,列表、字典、元组的转换传递,这里只是简单展示介绍了实现流程以及基础代码,用户模块以及文件处理里面也有涉及到自定义标签、数据校验、Django表单、文件和邮件处理,完整代码可以参考我的Github

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,757评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,478评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,540评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,593评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,903评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,329评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,659评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,383评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,055评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,337评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,864评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,227评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,820评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,999评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,750评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,365评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,260评论 2 258

推荐阅读更多精彩内容