打造一个自用的山寨喜马拉雅网站

匿名 (未验证) 提交于 2019-12-03 00:30:01

最近在听喜马拉雅有声书“侯卫东官场笔记”。

故事很吸引人,阿陈播的也非常到位。只是有一个痛点:每一集开头有长达40秒的片头介绍,声音非常大,而且每一集都重复。晚上躺在床上听的时候,会被这个片头震得耳朵疼,睡意全无。

所以就有了这个想法,能否实现跳过这个片头。于是捣鼓了一下,实现了下面这样的功能。

1. 访问自己在腾讯云买的服务器。

2. 打开网页,从第42秒自动开始播放,播放结束后,自动播放下一集。

效果如下:


以下记录以下主要的实现:

在此之前你需要有:

1. 一个阿里云或者腾讯云服务器

2. 服务器配置django,nginx, uwsgi


有了以上软硬件,就可以开始撸起袖子开干:

1. 通过albumId获取所有的 "index", "trackId"

albimid对应的是专辑(侯卫东官场笔记)

index对应每一集的序号(第108集)

trackid对应每一集的音频文件id(1320317, 需要通过这个id去获取音频文件的下载地址)

主要接口通过分析喜马拉雅network请求获得:

a, 获取每页的data数据(其中包括index, trackid)



headers = {     'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',     'Accept-Encoding': 'gzip, deflate',     'Accept-Language': 'zh-CN,zh;q=0.9',     'Cache-Control': 'max-age=0',     'Connection': 'keep-alive',     'Host': 'www.ximalaya.com',     'If-None-Match': 'W/"2cc08-HvI5ufGZ9TNYyyZOgJLO8mPSV64"',     'Upgrade-Insecure-Requests': '1',     'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36'  }    def getAllIDs(page):     s = requests.session()     ret = s.get(url=page,headers=headers).content.decode('utf-8')     j=json.loads(ret)     tracks=j['data']['tracks']     ret=[]     for track in tracks:         ret.append('{},{}'.format(track['index'], track['trackId']))     return ret

b. 遍历所有分页,传入上面的接口


def getAllPages():     allPages = []     basePage = 'http://www.ximalaya.com/revision/album/getTracksList?albumId=3071669&pageNum='     for i in range(1, 25):         page = basePage + '{}'.format(i)         allPages.append(page)     return allPages

c. 通过trackid获取对应的media文件

def getDownloadUrl(index,id):     print('{},{} starting'.format(index,id))     s=requests.session()     ret=s.get(url='http://www.ximalaya.com/revision/play/tracks?trackIds='+id,headers=headers).content     j=json.loads(ret)     src= j['data']['tracksForAudioPlay'][0]['src']     print('{},{},{} ending'.format(index, id,src))     return '{},{},{}\n'.format(index,id,src)

d. 保存 序号和音频url到数据库中

在django model中:

class XimalayaMedia(models.Model):     """     喜马拉雅media     """     name = models.CharField(max_length=4, verbose_name="专辑名称")     index=models.IntegerField(verbose_name=u'序号')     xmlyid=models.IntegerField(verbose_name=u'喜马拉雅id')     url = models.CharField(max_length=500, verbose_name="地址")     localurl=models.CharField(max_length=500, verbose_name="本地地址",default='')     add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")      class Meta:         verbose_name = "喜马拉雅音频"         verbose_name_plural = verbose_name      def __str__(self):         return "{}-{}".format(self.name,self.index)

至此实现了爬取每一集对应的音频文件地址,并保存到数据库中。效果如下:


2. 接下来通过网页访问音频文件,并设置开始播放时间和自动播放。

由于音频文件是m4a格式的,所以这里比较麻烦。由于html默认的audio支持的是mp3格式,所以没办法直接使用音频地址。

我的解决方法是,服务器后天去下载对应的音频流,然后保存成mp3格式,再通过网页访问位于自己服务器的mp3文件。

a. 前端html:

{% load staticfiles %} <html> <style>     .mainContent {         width: 100%;         min-height: 100vh;         background-color: #ffff;         margin: 0 auto;     }      .mid-content {         text-align: center;         padding: 50px 0;         width: 1200px;         height: 600px;         margin: 0 auto;     }      .header {         font-family: 'PingFangSC-Medium', 'Microsoft YaHei', sans-serif;         text-align: center;         color: #ffffff;         font-size: 20px;         font-weight: 700;     }      input::-webkit-outer-spin-button,     input::-webkit-inner-spin-button {         -webkit-appearance: none !important;         margin: 0;     }      .nextPre {         color: #262728;         font-size: 12px;         cursor: pointer;     } </style> <body> <div class="mainContent">     <div class="mid-content">         {#        <img style="display: none;width: 600px" src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1529053949564&di=9fad90c910eb153bd5ff179d5f6827cb&imgtype=0&src=http%3A%2F%2Fimgsrc.baidu.com%2Fimgad%2Fpic%2Fitem%2Fac345982b2b7d0a2f4bb5ca6c0ef76094b369a9f.jpg">#}         <div>             <h2>第{{ media.index }}集</h2>             <img src="http://imagev2.xmcdn.com/group8/M05/C8/8D/wKgDYVZqgCKRiQk1AAdB3O60pYc894.jpg!op_type=5&upload_type=album&device_type=ios&name=mobile_large&strip=0&quality=7">         </div>         <div class="audioList" style="margin-top: 20px">              <audio class="myaudio" controls="controls">                 <source src="{{ MEDIA_URL }}{{ media.localurl }}" type="audio/mp3">                 Your browser does not support the audio element.             </audio>         </div>         <div style="width: 270px;margin: 20px auto;">             <label class="nextPre" data-type="pre" style="float: left">上一首</label>             <label class="nextPre btnnext" data-type="next" style="float: right">下一首</label>             <div style="clear: both"></div>          </div>     </div> </div> </body> <script src="/static/js/jquery-2.2.3.js"></script> <script>      $().ready(function () {         $('.nextPre').click(function () {             var type = $(this).data('type');             var location = "";             if (type == "pre") {                 location = "{% url 'xmly' pre %}";             } else {                 location = "{% url 'xmly' next %}";             }             window.location.href=location;         });         {#        var myplayer = new MyPlayer().init();#}         var audio = $('.myaudio')[0];         audio.loop = false;         audio.currentTime = 42;         audio.playbackRate=1.3;         audio.play();         audio.addEventListener('ended', function () {             //自动播放下一页的             var lista = window.location.href.split('/');             var next = parseInt(lista[lista.length - 1]) + 1;             lista[lista.length - 1] = next;             window.location.href = lista.join('/');         }, false);     }); </script> </html>

b. url 设置路由

    url(r'^xmly/(?P<id>\d+)$', IndexView.as_view(), name='xmly'),

c. IndexView中:

class IndexView(View):     def get(self, request,id):         def getDownloadUrlFromLocal():             with open('F:/2.txt', 'r') as f:                 return f.readlines()          def retriveMedia(index, url):             print('starting download {}.mp3 from {}'.format(index, url))             s = requests.session()             ret = s.get(url).content             filepath='{}-{}.mp3'.format(media.name, media.index)             localurl = os.path.join(MEDIA_ROOT, filepath)             with open(localurl, 'wb') as f:                 f.write(ret)             print('end download {}.mp3 from {}'.format(index, url))             return filepath         media=XimalayaMedia.objects.filter(index=id).first()         if media.localurl=="":             executor = ThreadPoolExecutor(max_workers=1)             tasks=[]             task = executor.submit(retriveMedia, index=media.index,url=media.url)             tasks.append(task)             for future in as_completed(tasks):                 media.localurl = future.result()                 media.save()         pre=media.index-1         next=media.index+1         return render(request, 'index.html', locals())


关于优化:

后续可以继续优化:

1. 再优化接口,比如,只需要传入喜马拉雅某专辑的albumid, 以后要听其他专辑的话,就会更加自动化。

2. 如果能直接使用m4a文件,在前端访问就好了,这样就不需要再去下载。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!