这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

API接口及防盗链使用

转码端API,CMS端API,时间戳防盗链对接,图片加密解密对接,上传下载入库及通知接口,全闭环转码等讲解。

1 - CMS端API使用

该API包括大部分CMS端数据获取,视频获取、图集获取、标签分类获取、视频m3u8等相关信息。

API路由文件routes/index.js,涉及到的文件controller/cms.js,可自行修改api返回等。

以下API2大部分都是需要传递APIKEY的,使用前推荐在header中添加{token: apikey}

获取分类、标签、标签管理

Get /api2/gettags

#返回信息
{
    "tags": [
        {
            "counts": 2,
            "_id": "6087860146a58264c794d70d",
            "tag": "邱淑贞",
            "groupid": {
                "_id": "6046dd714b180d7bcd0d0b4b",
                "title": "人物"
            }
        },
        {
            "counts": 2,
            "_id": "6087860146a58264c794d716",
            "tag": "张敏"
        }
    ],
    "categories": [
        {
            "_id": "60d872f766c8886d0a95caec",
            "title": "动漫",
            "createAt": "2021-06-27T12:45:43.361Z",
            "__v": 0
        }
    ],
    "taggroups": [
        {
            "_id": "6046dd714b180d7bcd0d0b4b",
            "title": "人物",
            "createAt": "2021-03-09T02:29:05.128Z",
            "__v": 0
        }
    ]
}

#参数说明
tags为标签,counts为标签出现数量,groupid为该标签已添加到的标签管理(未添加到标签管理的标签则不存在此字段)
categories为视频分类
taggroups为标签管理

全套数据获取总API

GET /api2/getcontents

#调用方法,且只能获取到已经发布的电影、剧集、图集
/api2/getcontents?page=1&size=18&category=&tag=&tags=&user=&type=movie,tv&q=&order=countDesc

#调用参数信息
size是返回符合所有条件的数量,如果我同时返回type的电影、剧集类型,则每个类型都会返回size个,总返回数最大为size乘以type类型个数。
page为返回页码,比如api会返回符合条件的视频/剧集,然后获取总数会根据size切割成多个页码,page就是返回其中一个页码
type指定返回类型,比如movie为电影,tv为剧集,图集为image,如果你想多个一起返回,则使用英文逗号分开,如type=movie,tv
category是分类,且目前仅视频/剧集支持文字参数;图集只能通过其分类ID获取。
tag标签,且目前仅支持视频/剧集
tags标签数组(英文,分开),采用智能推荐算法,即多个标签命中权限排序,标签使用得当可根据用户关注返回完全相同的内容,目前支持视频/剧集、图集。
user用户ID,返回指定用户ID上传的视频数据
q搜索,返回符号该搜索关键词的内容,且目前仅支持视频/剧集
order为排序方式,为countDesc、countAsc、createAsc和createDesc,分别为播放/浏览次数倒序、播放/浏览次数正序、创建时间正序、创建时间倒序。

#调用说明
category tag q tags user这5种参数不能一起用,只能存在一个,type page size order是公用的,都为可选参数

调用示例如下:

#返回最新的电影十个数据
/api2/getcontents?page=1&size=10&type=movie

#返回最新的电影和剧集各十个数据
/api2/getcontents?page=1&size=10&type=movie,tv

#返回恐怖片分类中的电影和剧集中最新的十个数据
/api2/getcontents?page=1&size=10&type=movie,tv&category=恐怖片

#返回标签为日本的电影和剧集中最新的十个数据
https://www.leimulamu.com/api2/getcontents?page=1&size=10&type=movie,tv&tag=日本

#根据标签日本、动漫、科幻智能推荐算法API返回视频和剧集中最靠前的十个数据
/api2/getcontents?page=1&size=10&type=movie,tv&tags=日本,动漫,科幻

#返回搜索关键词龙珠的视频和剧集中最靠前的十个数据
https://www.leimulamu.com/api2/getcontents?page=1&size=10&type=movie,tv&q=龙珠

#返回指定用户ID上传的视频数据
/api2/getcontents?page=1&size=10&type=movie,tv&user=5c5e974c17a9ef6920622b3d

#返回图集类型最新的10个数据
/api2/getcontents?page=1&size=10&type=image  

返回信息如下:

#电影返回,poster2封面2信息,count播放次数,_id电影id,tags标签,category分类,previewvideo预览视频,poster封面1
[
    {
        "poster2": {
            "url": "/videos/202008/27/5f4761b75a92e955b55052e4/poster.jpg",
            "height": 814,
            "width": 540
        },
        "count": 15000,
        "tags": [
            "喜剧",
            "动作",
            "恐怖"
        ],
        "_id": "5f4761b75a92e955b55052e4",
        "originalname": "鬼打鬼之黄金道士",
        "createAt": "2021-04-27T04:49:36.708Z",
        "category": "电影",
        "previewvideo": "/videos/202008/27/5f4761b75a92e955b55052e4/preview.mp4",
        "poster": "/videos/202008/27/5f4761b75a92e955b55052e4/cover.jpg"
    }
]

#剧集返回,poster2和poster均为封面信息,_id剧集id,count播放次数,tags标签,category分类,episodes剧集信息(episode单集标题,movieid单集视频ID)
[
    {
        "poster2": {
            "url": "/poster/5ca0de597b64b73b0111165b/poster2.jpg",
            "width": 600,
            "height": 888
        },
        "count": 27252,
        "tags": [
            "喜剧",
            "科幻",
            "动画",
            "奇幻"
        ],
        "_id": "5ca0de597b64b73b0111165b",
        "title": "爱,死亡和机器人",
        "episodes": [
            {
                "_id": "5ca0dea919cc103abac8a49a",
                "episode": "01",
                "movieid": "5ca052227b64b73b0111162e"
            },
            {
                "_id": "5ca0dea919cc103abac8a499",
                "episode": "02",
                "movieid": "5ca052227b64b73b01111641"
            }
        ],
        "createAt": "2020-08-25T09:46:25.990Z",
        "poster": "/poster/5ca0de597b64b73b0111165b/poster.jpg",
        "category": "剧集"
    }
]

#图集信息,poster2和poster均为封面信息,images为全部图片地址
[
    {
        "poster2": {
            "url": "/images/5ee35ad291c8d479a076db75/poster2.jpg",
            "width": 600,
            "height": 1233
       },
        "images": [
            "/images/5ee35ad291c8d479a076db75/0.jpg",
            "/images/5ee35ad291c8d479a076db75/2.jpg",
        ],
        "_id": "5ee35ad291c8d479a076db75",
        "title": "水手服cosplay,清纯可爱性感",
        "createAt": "2020-06-12T10:37:06.083Z",
        "poster": "/images/5ee35ad291c8d479a076db75/poster.jpg"
    }
]

#文章咨询信息,poster2和poster均为封面信息,content为文章内容
[
    {
        "poster2": {
            "url": "/uploads/poster-file-1589959111721.jpg",
            "width": 600,
            "height": 375
        },
        "_id": "5d05fd3984023b28ca7996c2",
        "title": "二次元文化正在俘获亿万青年,二次元与主流文化融合加速",
        "content": "<p>文章内容</p>\r\n",
        "createAt": "2019-06-16T08:26:33.826Z",
        "poster": "/uploads/file-1589959111721.jpg"
    }
]

由于考虑到api效率问题,视频/剧集的描述返回都精简了,如有需求可联系我们单独为你加上该参数返回。

获取视频播放信息

Get /api2/getvideo/id  //id为movieid即电影id
Header {'token': apiKey}

#返回信息,下面全部的域名地址由转码设置的绑定域名决定,可自行修改
{
    "photos": [
        "https://videos.efvcms.com/videos/60e013d3bbf8db7bf9d747df/1.gif",
        "https://videos.efvcms.com/videos/60e013d3bbf8db7bf9d747df/0.jpg",
        "https://videos.efvcms.com/videos/60e013d3bbf8db7bf9d747df/1.jpg",
        "https://videos.efvcms.com/videos/60e013d3bbf8db7bf9d747df/2.jpg",
        "https://videos.efvcms.com/videos/60e013d3bbf8db7bf9d747df/3.jpg",
        "https://videos.efvcms.com/videos/60e013d3bbf8db7bf9d747df/4.jpg"
    ],
    "m3u8": "https://videos.efvcms.com/videos/60e013d3bbf8db7bf9d747df/20000/index.m3u8",
    "id": "60e013d3bbf8db7bf9d747df",
    "count": 105,
    "title": "明日之战",
    "summary": "克里斯·帕拉特商谈主演科幻影片[幽灵征募](Ghost Draft,暂译),克里斯·麦凯([乐高大电影])执导,扎克·迪恩([陷阱])操刀剧本。故事讲述一对父子被征募到一场未来战争中,这场战争决定着人...",
    "category": "美剧",
    "likes": [
        {
            "count": 16321,
            "_id": "60068fa654a23c201cd0855b",
            "originalname": "许嵩 - 断桥残雪",
            "poster": "https://efv.zhuanma.co/videos/202101/19/60068fa654a23c201cd0855b/cover.jpg"
        }
    ]
}

#参数说明
photos 为截图信息
m3u8 为m3u8链接
count 为视频播放次数
summary 为视频描述
likes 为推荐列表,根据标签推荐相似的视频
category 为视频分类

播放页获取视频信息

#id为视频id
Get /api2/getplay?type=movie|tv&id=id
Header {'token': apiKey}

response {
    movie,
    success,
    tv,   //如果type是tv才会有此返回,返回视频所属剧集详细信息。
    photos
}

搜索视频

Get /api2/getcontents?q=keywords&page=page&size=size&type=movie,tv

response {
    movies  //视频列表
}

热门视频

Get /api2/gethots

response {
    movies
}

返回日期分类的周、月、年热门视频

Get /api2/gethotsbydate

response {
    allhotmovies,
    monthhotmovies,
    yearhotmovies,
    weekhotmovies,
    host,
}

获取随机视频,剧集列表

Get /api2/getrandom
Header {'token': apiKey}

response {
    movies,
    host
}

获取图集列表

Get /api2/getimages?count=12&page=1

response {
    host, 
    images, 
    pages
}

获取图集漫画详情

Get /api2/getimage?id=id

response {
    host,
    image
}

获取视频下载价格

Get /api2/getdownloadprice?token=token&id=id

response {
    success: 0|1|2
    path //如果已经购买,并且在时限内则直接返回下载链接
    price //未购买或过期返回积分购买下载权限价格
}

积分购买下载权限

Get /api2/appdownload?token=token&id=id

response {
    success 0|1,
    message,
    path
}

获取全局导航信息和前端相关设置

Get /api2/getnav

response {
    categories,
    globaltags,
    portal
}

根据登录token,判断是否过期或者失效

Post /api2/checkauth

data {
    token
}

response {
    auth: true|false,
    user
}

获取标签列表,标签分组及全部分类

Get /api2/gettags

response {
    tags,
    categories,
    taggroups
}

API注册接口

Post /api2/postregister

data {
    username,
    email,
    password
}

response {
    success: 0|1,
    message,
    token,
    user
}

API登录接口

Post /api2/postlogin

data {
    email,
    password
}

response {
success: 0|1,
token,
user,
userfollow //用户关注的标签,用于智能推荐算法推荐相关关注内容
}

签到接口

Post /api2/checkin

data {
    token
}

response {
    success,
    message
}

获取付费用户组接口

Get /api2/getvipgroups

response {
    success:1,
    vipgroups
}

获取VIP用户组的价格

Get /api2/getjiage?group=groupid&duration=30

response {
    score, 
    price
}

使用积分购买VIP用户组接口

Post /api2/scorebuyvip

data {
    token,
    vipgroup,
    duration
}

response {
    success: 1|0,
    message
}

API直接使用支付接口购买VIP用户组

Post /api2/directbuyvip

data {
    group,
    duration,
    token
}

response {
    success,
    message,
    id   //订单ID,用于跳转订单页面进行支付
}

获取订单详情API

Get /api2/getitem?id=id

response {
    success: 1,
    item,
    pay
}

API发起码支付

Post /api2/codepay

data {
    id
}

response {
    success,
    url, //支付页面链接
    message
}

获取用户信息

Get /api2/getuser?id=id

response {
    success: 1|0 ,
    user
}

API发起购买积分

Post /api2/buyscore
    
data {
    token,
    score
}

response {
    success: 1|0,
    id, //订单ID
    message
}

使用卡劵的API接口

Post /api2/usecard

data {
    card,
    token
}

response {
    success,
    message
}

获取视频m3u8信息

#type参数,320为240P、480为360P、640为480P、1138为640P、1280为720P、1920为1080P、2560为2K、20000为原画320为240P、480为360P、640为480P、1138为640P、1280为720P、1920为1080P、2560为2K、20000为原画
Get /api2/getm3u8?type=320&id=id&token=token

response {
    success: 1|0|2,
    message,
    hd,  //返回分辨率
    m3u8
}

API积分点播视频

Post /api2/buymovie

data {
    id,
    token
}

response {
    success,
    message
}

切换用户关注标签的接口,若关注则取关,若没关注则关注

Post /api2/toggleuserfollow

data {
    tag,
    token
}

response {
    success,
    message
}

此套接口做了严格的数据检验,所以可放心使用,包含了从数据展示,获取,到购买,支付接口,积分购买,下载,积分点播等所有方方面面,可自行测试。

2 - 转码端API使用

该API包括部分转码端数据处理,生成截图、获取试看m3u8、实时生成mp4、远程创建转码任务等。

以下API使用中所提到的path参数不仅可用于服务器上的视频文件,亦可用于任何远程链接形式存在的视频文件,支持格式有以下几种:

/path/2.m3u8
./public/videos/3.mp4
public/videos/4.m3u8
http://127.0.0.1:3000/videos/202006/12/5ed37e23d6334d5540c43b00/d1052c/index.m3u8
http://127.0.0.1:3000/videos/6.mp4

所用到的apikeyapisecret参数均在转码设置中设置,且需打开api

生成单张截图

post: /apifluent/screenshot

body: {
apikey,
apisecret,
path,  // 支持服务器视频文件绝对路径,服务器视频相对路径,服务器源码目录相对路径,远程m3u8连接,远程视频文件链接等。
duration,  // 需要截图视频什么时间的画面,单位是秒。
}

response: {
success: 0|1,  // 成功或者失败
error,   // 如果失败会返回错误信息。
screenshot,  // 截图的链接。
}

批量生成截图

post: /apifluent/screenshots

body: {
apikey,
apisecret,
path,
counts  //截图数量,会根据视频时间轴,均分截取,比如10秒视频,设置counts为3,则可能截图3,6,9秒的画面。
}

response: {
success: 0|1,
error,  //如果有错误信息会返回错误信息。
screenshots, //数组,包含所有截图链接。
}

生成动态图

post: /apifluent/togif

data: {
apikey,
apisecret,
path,  //必填,下面的参数选填。
duration, // 生成多少秒动态图,默认1秒
width, //动态图宽度,高度自适应, 默认400宽度
gifstart, // 从视频多少秒开始生成, 默认10秒
auto  //是否自动截取视频最中间位置,接收1或0,设置了auto,则gifstart是否设置都无效,默认是0。
}

response: {
success: 0|1,
error,
gif //动态图链接
}

获取视频元信息,即metadata信息

post: /apifluent/metadata

body: {
apikey,
apisecret,
path
}

response: {
success: 1|0,
error,
data  // 视频的各种信息,包括音频流,视频流等。
}

data返回样式样式:

{
    "success": 1,
    "data": {
    "streams": [
            {
                "index": 0,
                "codec_name": "h264",
                "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
                "profile": "High",
                "codec_type": "video",
                "codec_time_base": "1/48",
                "codec_tag_string": "avc1",
                "codec_tag": "0x31637661",
                "width": 854,
                "height": 480,
                "coded_width": 864,
                "coded_height": 480,
                "has_b_frames": 2,
                "sample_aspect_ratio": "N/A",
                "display_aspect_ratio": "N/A",
                "pix_fmt": "yuv420p",
                "level": 30,
                "color_range": "unknown",
                "color_space": "unknown",
                "color_transfer": "unknown",
                "color_primaries": "unknown",
                "chroma_location": "left",
                "field_order": "unknown",
                "timecode": "N/A",
                "refs": 1,
                "is_avc": "true",
                "nal_length_size": 4,
                "id": "N/A",
                "r_frame_rate": "24/1",
                "avg_frame_rate": "24/1",
                "time_base": "1/24",
                "start_pts": 0,
                "start_time": 0,
                "duration_ts": 1253,
                "duration": 52.208333,
                "bit_rate": 537875,
                "max_bit_rate": "N/A",
                "bits_per_raw_sample": 8,
                "nb_frames": 1253,
                "nb_read_frames": "N/A",
                "nb_read_packets": "N/A",
                "tags": {
                    "creation_time": "1970-01-01T00:00:00.000000Z",
                    "language": "und",
                    "handler_name": "VideoHandler"
                },
                "disposition": {
                    "default": 1,
                    "dub": 0,
                    "original": 0,
                    "comment": 0,
                    "lyrics": 0,
                    "karaoke": 0,
                    "forced": 0,
                    "hearing_impaired": 0,
                    "visual_impaired": 0,
                    "clean_effects": 0,
                    "attached_pic": 0,
                    "timed_thumbnails": 0
                }
            },
            {
                "index": 1,
                "codec_name": "aac",
                "codec_long_name": "AAC (Advanced Audio Coding)",
                "profile": "LC",
                "codec_type": "audio",
                "codec_time_base": "1/48000",
                "codec_tag_string": "mp4a",
                "codec_tag": "0x6134706d",
                "sample_fmt": "fltp",
                "sample_rate": 48000,
                "channels": 2,
                "channel_layout": "stereo",
                "bits_per_sample": 0,
                "id": "N/A",
                "r_frame_rate": "0/0",
                "avg_frame_rate": "0/0",
                "time_base": "1/48000",
                "start_pts": 0,
                "start_time": 0,
                "duration_ts": 2493440,
                "duration": 51.946667,
                "bit_rate": 126694,
                "max_bit_rate": 128000,
                "bits_per_raw_sample": "N/A",
                "nb_frames": 2435,
                "nb_read_frames": "N/A",
                "nb_read_packets": "N/A",
                "tags": {
                    "creation_time": "1970-01-01T00:00:00.000000Z",
                    "language": "und",
                    "handler_name": "SoundHandler"
                },
                "disposition": {
                    "default": 1,
                    "dub": 0,
                    "original": 0,
                    "comment": 0,
                    "lyrics": 0,
                    "karaoke": 0,
                    "forced": 0,
                    "hearing_impaired": 0,
                    "visual_impaired": 0,
                    "clean_effects": 0,
                    "attached_pic": 0,
                    "timed_thumbnails": 0
                }
            }
        ],
        "format": {
            "filename": "https://media.w3.org/2010/05/sintel/trailer.mp4",
            "nb_streams": 2,
            "nb_programs": 0,
            "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
            "format_long_name": "QuickTime / MOV",
            "start_time": 0,
            "duration": 52.209,
            "size": 4372373,
            "bit_rate": 669979,
            "probe_score": 100,
            "tags": {
                "major_brand": "isom",
                "minor_version": "512",
                "compatible_brands": "isomiso2avc1mp41",
                "creation_time": "1970-01-01T00:00:00.000000Z",
                "title": "Sintel Trailer",
                "artist": "Durian Open Movie Team",
                "encoder": "Lavf52.62.0",
                "copyright": "(c) copyright Blender Foundation | durian.blender.org",
                "description": "Trailer for the Sintel open movie project"
            }
        },
        "chapters": []
    }
}

获取试看m3u8格式文本

GET /apifluent/try

headers: {
    apikey,
    apisecret,
}

query: {
    id:视频ID,
    duration:试看时长,
    hd:指定分辨率,
}

response: m3u8文本  //获取到文本可自行组装自己的m3u8路由。

hd参数值,320240P480360P640480P1138640P1280720P19201080P25602K20000为原画。

hdduration参数可选,默认60秒试看和最大分辨率,该接口会返回试看m3u8的文本内容,请自行构建自己的m3u8路由,勿直接前端使用本接口。

根据视频id和分辨率实时生成mp4

post: /apifluent/savestream

data: {
apikey,
apisecret,
id,  //视频库中存在的转码完成的id,
hd //指定生成某分辨率,支持240,360,480,640,720,1080,1440,20000
}

response: {
success: 1|0,
host,  // 服务端域名
path,  // mp4链接
message // 显示信息
}

API可用于提供下载功能时,无需保存mp4,完全实时根据m3u8生成mp4文件,并且返回,秒级反馈。

远程检测转码服务器运行/连接状态

post: /apifluent/ping

data: {
apikey,
apisecret,
}

response: {
success: 1,  //1为运行并连接正常,
}

API可检测转码程序运行状态,或者和远程转码程序连接情况是否正常。

远程创建转码任务

post: /apifluent/createjob

data: {
apikey,
apisecret,
clientId,  //传递一个数据库id,即客户端辨识视频id,返回信息的时候会返回该id,让你对应并进行数据填充。
remotePath,  //远程视频地址,即可访问的视频地址
notifyUrl  //通知域名,转码完成的clientId、视频名称、m3u8链接、海报截图会自动回传到这个地址。
}

{
 "success": 1|0  //1为创建成功
}

API可远程创建任务,并调用远程连接转码,建议配合队列转码一起使用,这样创建任务后后,会自动转码,自动返回数据,无需后台操作。

回传数据参考:

{
  success: 1,
  movie: {
    count: 0,
    retry: false,
    director: [],
    writer: [],
    stars: [],
    country: [],
    tags: [],
    screenshots: [],
    _id: '5fe19c79cb8b853f256a458f',
    status: '转码完成',
    originalname: '远程测试.avi',
    path: './download/远程测试.avi',
    size: '112406774',
    notifyUrl: 'https://www.beefun.cc/api/payback',
    clientId: '5fe19c7888f5be253328806a',
    m3u8paths: [ [Object], [Object], [Object] ],
    thirdm3u8: [],
    createAt: '2020-12-22T07:15:47.300Z',
    __v: 0,
    duration: '3分钟',
    moviepath: '/videos/202012/22/5fe19c79cb8b853f256a458f',
    previewvideo: '/videos/202012/22/5fe19c79cb8b853f256a458f/preview.mp4',
    poster: '/videos/202012/22/5fe19c79cb8b853f256a458f/cover.jpg'
  },
  apikey: '5c20cEga22FgG7f7B1c1',
  apisecret: 'cd5c5ffd476f93602135'
}
[
  [Object: null prototype] {
    id: '5fc7242b0446292f13401fe2',
    domain: 'https://demo.efvcms.com'
  }
]
{
  title: '远程测试.avi',
  url: '240P$https://demo.efvcms.com/videos/202012/22/5fe19c79cb8b853f256a458f/3413b2/index.m3u8#480P$https://demo.efvcms.com/videos/202012/22/5fe19c79cb8b853f256a458f/927817/index.m3u8#',
  server: { connect: { id: '5fc7242b0446292f13401fe2' } }
}

远程创建转码任务并对视频进行裁剪截取

//注意如果远程视频够大,可能需要耐心等待片刻
post: /apifluent/cut

data: {
apikey,
apisecret,
start, //裁剪起始时间,规范为时:分:秒,如 00:00:12
end, //裁剪结束时间,规范为时:分:秒,如 00:00:22
video,  //远程视频地址,即可访问的视频地址
notifyUrl  //通知域名,转码完成的id、视频名称、m3u8链接、海报截图会自动回传到这个地址。
}

{
 "success": 1|0  //1为创建成功
}

API可远程创建任务,并调用远程连接转码,建议配合队列转码一起使用,这样创建任务后后,会自动转码,自动返回数据,无需后台操作。

回传数据参考:

#返回数据仅供参考,实际情况可能会有不一样
{
  success: 1,
  movie: {
    count: 0,
    retry: false,
    director: [],
    writer: [],
    stars: [],
    country: [],
    tags: [],
    screenshots: [],
    _id: '5fe19c79cb8b853f256a458f',
    status: '转码完成',
    originalname: '远程测试.avi',
    path: './download/远程测试.avi',
    size: '112406774',
    notifyUrl: 'https://www.beefun.cc/api/payback',
    clientId: '5fe19c7888f5be253328806a',
    m3u8paths: [ [Object], [Object], [Object] ],
    thirdm3u8: [],
    createAt: '2020-12-22T07:15:47.300Z',
    __v: 0,
    duration: '3分钟',
    moviepath: '/videos/202012/22/5fe19c79cb8b853f256a458f',
    previewvideo: '/videos/202012/22/5fe19c79cb8b853f256a458f/preview.mp4',
    poster: '/videos/202012/22/5fe19c79cb8b853f256a458f/cover.jpg'
  },
  apikey: '5c20cEga22FgG7f7B1c1',
  apisecret: 'cd5c5ffd476f93602135'
}
[
  [Object: null prototype] {
    domain: 'https://demo.efvcms.com'
  }
]
{
  title: '远程测试.avi',
  url: '240P$https://demo.efvcms.com/videos/202012/22/5fe19c79cb8b853f256a458f/3413b2/index.m3u8#480P$https://demo.efvcms.com/videos/202012/22/5fe19c79cb8b853f256a458f/927817/index.m3u8#',
  server: { connect: { id: '5fc7242b0446292f13401fe2' } }
}

远程删除视频库视频

delete: /apifluent/delete?id=5fcf24ed3fd3764f5b40f95f

data: {
apikey,
apisecret
}

{
 "success": 1|0  //1为删除成功
}

注意id=5fcf24ed3fd3764f5b40f95f,此idEFV中视频的id,在远程创建任务,并转码服务完成时会带上efv中的id传递到notifyUrl上,对接的系统接收到此id储存到自己的数据库中,在删除时就请求delete删除efv中的视频数据和切片图片等一切内容。

3 - 发布到其它CMS教程

目前支持发布到苹果CMS、WordPress,还支持批量导出表格、火车头采集、数据POST接口等第三方CMS使用。

批量导出Excel表格

目前可批量导出分类、分类标签、M3U8链接、封面和截图链接。

可前往程序后台 ⇒ 转码相关 ⇒ 视频管理,筛选出指定数量的视频,批量选择需要导出的视频,再点击下方的批量导出表格即可。

表格的M3U8和截图链接域名,需在转码设置的设置域名填入,即该参数为导出表格的域名显示。

开启火车头采集

可前往程序后台 ⇒ CMS相关 ⇒ CMS设置,开启采集页面即可,然后可通过火车头采集转码后的视频、截图等信息,采集地址/caiji

采集地址的M3U8和截图链接域名,需在CMS设置的CMS站点网址填入,即该参数为采集页面的域名显示。

数据POST通知接口

该功能可在视频转码完成后,会立即将m3u8链接等信息自动通知到指定域名,你只需要接收相关数据并入库到你自己的程序即可,接口使用 ⇒ 传送门

批量复制M3U8

目前可批量复制M3U8和封面链接。

可前往程序后台 ⇒ 转码相关 ⇒ 视频管理,筛选出指定数量的视频,批量选择需要复制的视频,再点击下方的批量复制M3U8即可。

复制顺序修改

我们在EFV后台使用批量复制M3U8功能的时候,默认排序为视频名称$对应分辨率m3u8$封面1,由于有些人需要调整排序,或者不需要封面,这里就说下怎么修改。

比如我安装目录/home/express-ffmpeg,则需要修改的文件为/home/express-ffmpeg/views/admin/admin.jade,编辑文件并搜索.copyall,在第二个匹配词处,需要修改的大概代码如下:

if(p240.length) {
  for(var i=0; i< p240.length; i++) {
    var p240movie = p240[i];
    p240message += p240movie.title +"$"+ url + p240movie.m3u8 +"$" + url + p240movie.poster+ "\r\n";
  }
}
if(p360.length) {
  for(var i=0; i< p360.length; i++) {
    var p360movie = p360[i];
    p360message += p360movie.title +"$"+ url + p360movie.m3u8 +"$" + url + p360movie.poster+ "\r\n";
  }
}
if(p480.length) {
  for(var i=0; i< p480.length; i++) {
    var p480movie = p480[i];
    p480message += p480movie.title +"$"+ url + p480movie.m3u8 +"$" + url + p480movie.poster+ "\r\n";
  }
}
if(p640.length) {
  for(var i=0; i< p640.length; i++) {
    var p640movie = p640[i];
    p640message += p640movie.title +"$"+ url + p640movie.m3u8 +"$" + url + p640movie.poster+ "\r\n";
  }
}
if(p720.length) {
  for(var i=0; i< p720.length; i++) {
    var p720movie = p720[i];
    p720message += p720movie.title +"$"+ url + p720movie.m3u8 +"$" + url + p720movie.poster+ "\r\n";
  }
}
if(p1080.length) {
  for(var i=0; i< p1080.length; i++) {
    var p1080movie = p1080[i];
    p1080message += p1080movie.title +"$"+ url + p1080movie.m3u8 +"$" + url + p1080movie.poster+ "\r\n";
  }
}
if(p2k.length) {
  for(var i=0; i< p2k.length; i++) {
    var p2kmovie = p2k[i];
    p2kmessage += p2kmovie.title +"$"+ url + p2kmovie.m3u8 +"$" + url + p2kmovie.poster+ "\r\n";
  }
}
if(poriginal.length) {
  for(var i=0; i< poriginal.length; i++) {
    var poriginalmovie = poriginal[i];
    poriginalmessage += poriginalmovie.title +"$"+ url + poriginalmovie.m3u8 +"$" + url + poriginalmovie.poster+ "\r\n";
  }
}

这里将具体需要修改的代码列举出来,每行代表的分辨率应该容易看得出来,如下:

p240message += p240movie.title +"$"+ url + p240movie.m3u8 +"$" + url + p240movie.poster+ "\r\n";
p360message += p360movie.title +"$"+ url + p360movie.m3u8 +"$" + url + p360movie.poster+ "\r\n";
p480message += p480movie.title +"$"+ url + p480movie.m3u8 +"$" + url + p480movie.poster+ "\r\n";
p640message += p640movie.title +"$"+ url + p640movie.m3u8 +"$" + url + p640movie.poster+ "\r\n";
p720message += p720movie.title +"$"+ url + p720movie.m3u8 +"$" + url + p720movie.poster+ "\r\n";
p1080message += p1080movie.title +"$"+ url + p1080movie.m3u8 +"$" + url + p1080movie.poster+ "\r\n";
p2kmessage += p2kmovie.title +"$"+ url + p2kmovie.m3u8 +"$" + url + p2kmovie.poster+ "\r\n";
poriginalmessage += poriginalmovie.title +"$"+ url + poriginalmovie.m3u8 +"$" + url + poriginalmovie.poster+ "\r\n";

这里列举常见删减例子,即不需要封面1,自己挨个覆盖,如下:

#该复制排序为 视频名称$对应分辨率m3u8
p240message += p240movie.title +"$"+ url + p240movie.m3u8 + "\r\n";
p360message += p360movie.title +"$"+ url + p360movie.m3u8 + "\r\n";
p480message += p480movie.title +"$"+ url + p480movie.m3u8 + "\r\n";
p640message += p640movie.title +"$"+ url + p640movie.m3u8 + "\r\n";
p720message += p720movie.title +"$"+ url + p720movie.m3u8 + "\r\n";
p1080message += p1080movie.title +"$"+ url + p1080movie.m3u8 + "\r\n";
p2kmessage += p2kmovie.title +"$"+ url + p2kmovie.m3u8 + "\r\n";
poriginalmessage += poriginalmovie.title +"$"+ url + poriginalmovie.m3u8 + "\r\n";

修改后,使用ssh客户端登录转码服务器,运行pm2 reload efvcms命令重启生效。

拓展修改

240P分辨率为例,完整代码如下:

p240message += p240movie.title +"$"+ url + p240movie.m3u8 +"$" + url + p240movie.poster+ "\r\n";
 
#参数详解
p240movie.title为视频标题
url + p240movie.m3u8为m3u8链接,参数组合为 域名链接 + m3u8路径
url + p240movie.poster为封面1链接,参数组合为 域名链接 + 封面1路径
 
#组装提示
最前面p240message += 和最后面+ "\r\n";不要修改,只需要调整中间位置就行,并用+连接起来,固定字符串$用双引号即"$"

比如我需要将M3U8批量复制到另一台EFV高级版进行下载, 而EFV后台下载中心批量添加的格式为视频名称$下载类型$m3u8链接,此时我可以进行如下修改:

#EFV最新版m3u8下载类型为downloadall,且$downloadall为固定字符串,需要用双引号,大致修改代码如下
p240message += p240movie.title +"$downloadall"+ "$"+ url + p240movie.m3u8 +"\r\n";

其它分辨率自己照葫芦画瓢,最后使用ssh客户端登录转码服务器,运行pm2 reload efvcms命令重启生效。

4 - 上传下载和数据入库及通知

转码下载及同步等统计接口、剧集和视频信息入库、视频文件入库、视频上传、下载中心、转码数据POST通知接口等。

统计信息

#后面为转码设置的apikey
Get /api2/videostatistics?apikey=5c20cEga22FgG7f

#返回信息
{
    "视频管理": {
        "自动转码": "已开启",
        "视频总数": 9,
        "等待转码": 0,
        "转码完成": 9,
        "转码错误": 0
    },
    "下载中心": {
        "自动下载": "已开启",
        "任务总数": 0,
        "下载完成": 0,
        "下载失败": 0
    },
    "云存储同步": {
        "同步状态": "已关闭",
        "可同步视频数": 9,
        "已同步视频数": 0
    },
    "硬盘信息": [
        {
            "分区": "/dev/mapper/VolGroup-lv_root",
            "挂载": "/",
            "硬盘总量": "911.63G",
            "使用量": "474.66G",
            "使用百分比": "52.07%"
        },
        {
            "分区": "/dev/md0",
            "挂载": "/boot",
            "硬盘总量": "0.94G",
            "使用量": "0.12G",
            "使用百分比": "12.63%"
        }
    ]
}

视频文件入库

#该api效果可参考扫描入库,可以将movies所有视频入库到视频库,再自动转码。
POST /api/ruku

body: {
    apikey,
}

response: {
    success: 1,
}

#curl命令使用示例,apikey可在转码设置处获取
curl http://127.0.0.1:3000/api/ruku -d "apikey=5c20cEga"

视频详情信息入库

提示:如果你的视频和封面都是链接形式,推荐使用下载中心api代替处理。

POST /api/postmovie

body: {
    #必填参数
    apikey, //api,转码设置获取
    originalname, //视频名称
    path, //视频路径,可为相对或绝对路径,相对路径以切片根目录为参考,如./movies/test.mp4

    #可选参数,如用不到相关参数可不用传递
    poster, //封面路径,不填会自动生成,仅支持jpg、png、jpeg、webp格式,可为相对或绝对路径,相对路径以切片根目录为参考,如./movies/test.png
    category, //视频分类,不存在会自动创建
    originaltitle, //视频原名
    aka, //别名
    language, //语言
    banben, //版本,hd等
    director, //导演,多个导演用英文逗号,分开,如 导演1,导演2,导演3
    stars, //主演,多个主演用英文逗号,分开,如 主演1,主演2,主演3
    writer, //编剧,多个编剧用英文逗号,分开,如 编剧1,编剧2,编剧3
    tags, //分类标签,多个标签用英文逗号,分开,如 剧情,悬疑,搞笑
    summary, //影片详情
    country, //国家
    year, //发行年份
    rate, //豆瓣评分

    #可选参数,可自动入库到剧集,下面3个都必填,如用不到可不用传递;且剧集id可以使用剧集api添加后获取
    autodownTv, //剧集id,填入后,会在转码完成时,自动添加进剧集
    autodownepisodes, //集数,如 01,会在添加进剧集后,自动排序;集数命名要规范,位数和总集数一致,如总集数2位数,则用01、10,3位数用001、010。
    tvepisodesname, //集数,如 01,
}

response: {
    success: 1|0,  //1成功,并会直接返回视频的id;0失败
}

视频详情信息更新

POST /api/postupmovie

body: {
    #必填参数
    apikey, //api,转码设置获取
    originalname, //视频名称的关键词或全称,为模糊搜索

    #可选参数,如用不到相关参数可不用传递
    poster,  //封面,仅支持jpg、png、jpeg、webp格式,可为相对或绝对路径,相对路径以切片根目录为参考,如./movies/test.png;也可以为链接,如为链接,则会自动下载并处理。
    category, //视频分类,不存在会自动创建
    originaltitle, //视频原名
    aka, //别名
    language, //语言
    banben, //版本,hd等
    director, //导演,多个导演用英文逗号,分开,如 导演1,导演2,导演3
    stars, //主演,多个主演用英文逗号,分开,如 主演1,主演2,主演3
    writer, //编剧,多个编剧用英文逗号,分开,如 编剧1,编剧2,编剧3
    tags, //分类标签,多个标签用英文逗号,分开,如 剧情,悬疑,搞笑
    summary, //影片详情
    country, //国家
    year, //发行年份
    rate, //豆瓣评分
}

response: {
    success: 1|0,  //1成功,并会直接返回更新视频的id和名称;0失败
}

剧集详情信息入库

POST /api/posttv

body: {
    #必填参数
    apikey, //转码设置获取
    title, //剧集标题
    poster,  //封面,仅支持jpg、png、jpeg、webp格式,可为相对或绝对路径,相对路径以切片根目录为参考,如./movies/test.png;也可以为链接,如为链接,则会自动下载并处理。

    #可选参数,如用不到相关参数可不用传递
    episodescount, //集数,如12
    duration, //时长,可随便填入某个单集时长,单位分钟,如 23
    category, //视频分类,不存在会自动创建
    originaltitle, //剧集原名
    aka, //剧集别名
    language, //语言
    director, //导演,多个导演用英文逗号,分开,如 导演1,导演2,导演3
    stars, //主演,多个主演用英文逗号,分开,如 主演1,主演2,主演3
    writer, //编剧,多个编剧用英文逗号,分开,如 编剧1,编剧2,编剧3
    tags, //分类标签,多个标签用英文逗号,分开,如 剧情,悬疑,搞笑
    summary, //剧集简介
    country, //发行地区
    year, //发行年份,如 1996
    rate, //豆瓣评分,如 8.8
}

response: {
    success: 1|0,  //1成功,并会直接返回剧集的id;0失败
}

上传

提示:如果不会使用分片上传,可以使用简易上传或使用下载代替上传。

分片上传

分片上传需要配合插件一起使用,目前推荐Web Uploader,传送地点:点击查看

#需配合webuploader,file是分片之后的视频文件,md5码是未分片前视频的独一无二的MD5码,size是视频总大小单位字节,chunk从0开始,chunks是总分片数
POST /api/upload

body: {
    uploadkey, //后台设置上传秘钥
    file, //文件对象
    filemd5, //md5码
    chunk, //当前分片数
    chunks, //总分片数
    size,  //文件大小
}

response: {
    success: 1,
    id, //返回上传成功的视频objectid,用于构建m3u8地址。
}

我们上传后,会自动返回一个视频objectid,我们可以直接拿来组装M3U8地址和截图地址,基本上都是固定路径。

注意上传如果报错413状态码,则可能是nginx上传限制,调整下nginx上传限制大小即可。

简易上传

POST /api/simpleupload  //可根据后台设置m3u8路径自己架构返回的m3u8地址。

body: {
    uploadkey, //上传凭证,转码设置下方获取
    file, //上传文件
}

response: {
    success: 1,
    id, //返回上传成功的视频objectid,用于构建m3u8地址。
}

#curl命令使用示例,uploadkey为上传凭证,可在转码设置处获取
curl http://127.0.0.1:3000/api/simpleupload?uploadkey=7e8EgFAe -F "file=@/root/xx.mp4"

我们上传后,会自动返回一个视频objectid,我们可以直接拿来组装M3U8地址和截图地址,基本上都是固定路径。

注意上传如果报错413状态码,则可能是nginx上传限制,调整下nginx上传限制大小即可。

下载中心

POST /api/downloadm3u8

body: {
    #必填参数
    apikey, //转码设置获取
    name, //视频名称,下载完成后自动入库到视频管理的视频名称
    url,  //下载链接,注意如果地址包含&等特殊符号,如果影响到使用,记得转义下
    type,  //下载类型,如m3u8、downloadall,且m3u8仅支持下载m3u8链接,downloadall支持m3u8、mp4、mkv等其它视频直链和youtube视频地址下载,建议下载M3U8首选m3u8类型,m3u8类型出错就使用downloadall类型下载M3U8。

    #可选参数,如用不到相关参数可不用传递
    poster,  //封面链接,封面仅支持jpg、png、jpeg、webp格式,下载转码后,会自动转换成封面1和封面2;如不填会自动生成封面。
    category, //视频分类,不存在会自动创建
    clientId, //传递一个数据库id,即客户端辨识视频id,当使用通知接口返回信息的时候会返回该id,让你对应并进行数据填充。
    geturl, //解析接口配置,设置详情看下方
    originaltitle, //视频原名
    aka, //视频别名
    language, //语言
    banben, //版本,如HD、高清等
    director, //导演,多个导演用英文逗号,分开,如导演1,导演2,导演3
    stars, //主演,多个主演用英文逗号,分开,如主演1,主演2,主演3
    writer, //编剧,多个编剧用英文逗号,分开,如编剧1,编剧2,编剧3
    tags, //分类标签,多个标签用英文逗号,分开,如剧情,悬疑,搞笑
    summary, //视频简介
    country, //发行地区
    year, //发行年份
    rate, //豆瓣评分

    #可选参数,可自动入库到剧集,下面3个都必填,如用不到可不用传递;且剧集id可以使用剧集api添加后获取
    autodownTv, //剧集id,填入后,会在转码完成时,自动添加进剧集
    autodownepisodes, //集数,如 01,会在添加进剧集后,自动排序;集数命名要规范,位数和总集数一致,如总集数2位数,则用01、10,3位数用001、010。
    tvepisodesname, //集数,如 01,
}

response: {
    success: 1|0,  //1成功,0失败
}

#curl命令使用示例
curl http://127.0.0.1:3000/api/downloadm3u8 -d "name=西游记&category=动作片&url=http://127.0.0.1:8080/1.m3u8&apikey=5c20cEga&type=m3u8&poster=http://127.0.0.1:8080/1.jpg

注意部分参数的格式,切勿乱填,且导入成功后,部分信息不会显示在下载中心,直到下载完成后,会自动将你填入的所有信息入库到视频管理。

解析接口配置

如果获取到的m3u8需要配合解析接口使用,那么得填入解析接口配置,格式为:解析参数$解析接口

比如我获取到的m3u8链接为:http://127.0.0.1:8080/1.m3u8,解析接口为:http://127.0.0.1:3000/api?url=

然后访问解析m3u8的地址,如:http://127.0.0.1:3000/api?url=http://127.0.0.1:8080/1.m3u8,返回json数据如下:

{
    code: '200',
    url: 'http://127.0.0.1:8080/9.m3u8',
    type: 'hls'
}

url参数为解析后获取到的m3u8链接,那么解析接口配置就填入:url$http://127.0.0.1:3000/api?url=,最后下载的时候,会提取解析后的m3u8进行下载。

通知域名获取转码信息

通知域名的作用就是可以将转码完成的视频所有详细信息传给通知域名。

先去EFV后台转码设置设置一个对接域名,开启转码成功通知即可,如果你想设置多个通知域名,中间请使用英文|分开。

且通知方式有2种,分为手动通知和转码自动通知,如下:

#自动通知
需前往转码设置开启转码完成自动通知,即视频转码完成后,立马会将m3u8链接等信息自动通知到指定域名。

#手动通知
如你想先编辑视频或剧集信息后再通知,则需关闭转码完成自动通知,然后编辑好了后,通过手动发布的方式通知。

注意开启转码完成自动通知后,手动发布通知会失效,建议测试的时候关闭转码完成自动通知,使用手动发布通知,手动发布通知后可以反复下架重新发布,多次通知测试,

EFV会将转码完成的视频详细信息JSON类型POST到这个域名,您在您的后台只需要针对这个接收API处理JSON数据入库您自己的第三方程序数据库即可。

相关信息:

对象名:movie

#获取到的相关参数信息
status: String,
size: String,
category: String,
originalname: String,
originaltitle: String,
aka: String,
language: String,
banben: String,
vtt: String,
thumbnails: String,
poster: String,
poster2: { url: String, width: Number, height: Number },
count: { type: Number, default: 0 },
retry: { type: Boolean, default: false},
path: String,
duration: String,
director: [String],
writer: [String],
rate: Number,
year: Number,
stars: [String],
summary: String,
country: [String],
tags: [String],
m3u8paths: [{ hd: Number, path: String }],
previewvideo: String,
price: Number,
totv: Schema.Types.ObjectId,
md5: String,
createAt: {
  type: Date
}

如果你使用了详细编辑,里面的数据都有,接收API就选择里边的数据处理就行了。

单个视频接收的JSON数据

{
poster2: {
  url: '/videos/202202/07/61ffbfbb9b36dd00c878b4a1/poster.jpg',
  height: 773,
  width: 540
},
count: 23,
retry: false,
autoPublish: false,
director: [ '古桥一浩' ],
writer: [ '古桥一浩' ],
stars: [
  '三宅健太',  '宫野真守', '樱井孝宏',
  '伊藤静',   '梶裕贵',  '绵贯龙之介',
  '濑户麻沙美', '早见沙织', '滨野大辉',
  '柳田淳一',  '铃木玲子', '石冢运升',
  '中井和哉',  '天崎滉平', '菅原慎介',
  '町山芹菜'
],
country: [ '日本' ],
tags: [ '剧情', '喜剧', '战争', '动画' ],
screenshots: [
  '/videos/202202/07/61ffbfbb9b36dd00c878b4a1/0.jpg',
  '/videos/202202/07/61ffbfbb9b36dd00c878b4a1/2.jpg',
  '/videos/202202/07/61ffbfbb9b36dd00c878b4a1/1.jpg',
  '/videos/202202/07/61ffbfbb9b36dd00c878b4a1/3.jpg'
],
_id: '61ffbfbb9b36dd00c878b4a1',
originalname: '窈窕淑女前篇:红绪,花样的17岁',
status: 'finished',
path: './movies/Haikara-san ga Toru the movie.mkv',
category: '日本动漫',
size: '5497612960',
createAt: '2022-02-16T09:25:49.733Z',
m3u8paths: [
  {
    _id: '6200c8949dc15a00c15be7db',
    hd: 640,
    path: './public/videos/202202/07/61ffbfbb9b36dd00c878b4a1/8f89fb/index.m3u8'
  },
  {
    _id: '6200cc499dc15a00c15be809',
    hd: 1280,
    path: './public/videos/202202/07/61ffbfbb9b36dd00c878b4a1/08cad8/index.m3u8'
  }
],
thirdm3u8: [],
__v: 1,
height: 1080,
width: 1920,
duration: '97分钟',
moviepath: '/videos/202202/07/61ffbfbb9b36dd00c878b4a1',
gif: '/videos/202202/07/61ffbfbb9b36dd00c878b4a1/1.gif',
previewvideo: '/videos/202202/07/61ffbfbb9b36dd00c878b4a1/preview.mp4',
poster: '/videos/202202/07/61ffbfbb9b36dd00c878b4a1/cover.jpg',
firstScreen: '/videos/202202/07/61ffbfbb9b36dd00c878b4a1/first.jpg',
aka: '',
banben: 'HD',
language: '日语',
originaltitle: '劇場版 はいからさんが通る 前編 ~紅緒、花の17歳~',
rate: 7.5,
summary: '根据70年代大受欢迎的少女漫画《窈窕淑女》改编的剧场版动画,《窈窕淑女》的题目字面意思出自诗经:“窈窕淑女,君子好逑&#34;。大和和纪的《窈窕淑女》是1977年的少女漫画优秀杰作,曾经红极一时。讲述了一个关...',
year: 2017
}

注意m3u8paths里边的hd参数,320240P480360P640480P1138640P1280720P19201080P25602K20000为原画。

剧集接收的JSON数据

由于剧集需要先整理分集,所以暂时没自动发布,只能通过手动点击正式发布,才会通知到域名,数据如下:

{
poster2: {
    url: '/poster/623eaaf9b1d50c9badb2f47b-poster2.jpg',
    height: 763,
    width: 540
},
count: 263,
director: [ '中山敦史', '山本裕介', '名和宗则', '森义博', '重原克也' ],
writer: [ '木村畅', '横手美智子' ],
stars: [
    '兴津和幸',   '小野大辅',   '伊藤静',
    '大原沙耶香', '麦人',       '田中敦子',
    '土师孝也',   '中村悠一',   '高桥李依',
    '稻田彻',     '绵贯龙之介', '野濑育二',
    '松冈祯丞',   '千本木彩花', '加隈亚衣',
    '田村由香里', '柳田淳一',   '相马康一',
    '手冢弘道',   '井上喜久子', '菅生隆之',
    '大桥彩香',   '朝井彩加',   '阪口大助',
    '樱井浩美',   '天崎滉平',   '菅原慎介',
    '木下浩之',   '千叶进步',   '松本忍',
    '松风雅也',   '岩泽俊树',   '内匠靖明',
    '木内太郎',   '伊丸冈笃',   '池田胜',
    '潘惠子',     '深川和征'
],
country: [ '日本' ],
tags: [ '动画', '奇幻' ],
published: 'waiting',
_id: '623eaaf9b1d50c9badb2f47b',
title: '骑士&魔法',
originaltitle: 'ナイツ&amp;マジック',
aka: "Knight's Magic",
year: 2017,
duration: '24分钟',
episodescount: '13',
language: '日语',
rate: 6.6,
summary: '能干的程序员,同时是重度机器人宅的青年,转生到了巨大机器人“幻晶骑士(Silhouette Knight)”震动大地的骑士与魔法的异世界!?以艾尔涅斯帝·埃切贝里亚的身份转生的他,活用丰富的机械知识...',
status: 'wanjie',
zhouqi: '',
episodes: [
    {
    _id: '62777218e7a65ed82225e5bc',
    episode: '01',
    movieid: '62010cfa471273018072295a'
    },
    {
    _id: '62777218e7a65ed82225e5bd',
    episode: '02',
    movieid: '62010cfa471273018072295b'
    },
    {
    _id: '62777218e7a65ed82225e5be',
    episode: '03',
    movieid: '62010cfa471273018072295c'
    },
    {
    _id: '62777218e7a65ed82225e5bf',
    episode: '04',
    movieid: '62010cfa471273018072295d'
    },
    {
    _id: '62777218e7a65ed82225e5c0',
    episode: '05',
    movieid: '62010cfa471273018072295f'
    },
    {
    _id: '62777218e7a65ed82225e5c1',
    episode: '06',
    movieid: '62010cfa4712730180722960'
    },
    {
    _id: '62777218e7a65ed82225e5c2',
    episode: '07',
    movieid: '62010cfa4712730180722961'
    },
    {
    _id: '62777218e7a65ed82225e5c3',
    episode: '08',
    movieid: '62010cfa4712730180722963'
    },
    {
    _id: '62777218e7a65ed82225e5c4',
    episode: '09',
    movieid: '62010cfa4712730180722964'
    },
    {
    _id: '62777218e7a65ed82225e5c5',
    episode: '10',
    movieid: '62010cfa4712730180722959'
    },
    {
    _id: '62777218e7a65ed82225e5c6',
    episode: '11',
    movieid: '62010cfa4712730180722965'
    },
    {
    _id: '62777218e7a65ed82225e5c7',
    episode: '12',
    movieid: '62010cfa471273018072295e'
    },
    {
    _id: '62777218e7a65ed82225e5c8',
    episode: '13',
    movieid: '62010cfa4712730180722962'
    }
],
createAt: '2022-05-08T07:32:40.856Z',
__v: 0,
poster: '/poster/623eaaf9b1d50c9badb2f47b-poster.jpg',
category: '日本动漫',
updateAt: '2023-01-16T16:01:40.804Z'
}

movieid为集数的视频id,可以通过拼接获取该集数的自适应m3u8地址,拼接如下:

#拼接格式:/movie/auto/ + 视频id + .m3u8
/movie/auto/62010cfa4712730180722962.m3u8

最后就可以获取每个集数的自适应m3u8链接了。

5 - 时间戳防盗链对接教程

强烈建议和TS加密功能一起使用,TS加密的key文件会自动加入到播放验证环节,从而防盗链会更有效。
提示:可通过自行对接 或 直接使用解析接口获取可访问的m3u8链接,按需求选择即可。

对接之前请先了解下时间戳+播放次数防盗链的原理及设置 ⇒ 传送门,目前M3U82种调用方法,如下:

1、使用自适应M3U8,格式为:/movie/auto/61f68205eeeec878ae3fe3d5.m3u8
2、使用具体分辨率的M3U8,如1080P,格式为:/videos/202201/31/61f68205eeeec878ae3fe3d5/217g7g/index.m3u8

可在视频管理复制M3U8,或者通知接口获取对应的链接,由于通知接口没直接推送自适应M3U8,需要自行组装,组装格式为/movie/auto/ + 视频id + .m3u8,且对自适应M3U8还不了解的可看 ⇒ 传送门

解析接口使用

解析接口需要前往转码相关 ⇒ 防盗链/图片加密设置,找到设置时间戳防盗链的json解析接口功能项,可填入指定参数组合开启该功能,留空则关闭。

且单个接口配置为:y或n过期时间$自定义接口key值$ip白名单,多个接口配置需使用英文冒号:分开,参数详解:

y为切片广告功能有效,n为切片广告功能无效,即如果你设置切片广告,则不会给你播放切片广告;后面接过期时间,如y2024-10-23,不填则一直有效
key值为任意字母数字组合,如kll323,那么接口地址为/api/m3u8?key=kll323&url=
ip白名单可不设置,如不设置则任何人都可以使用该解析接口,如你只想让某个ip请求,则填入该ip即可

设置参考:

#该配置为切片广告功能有效,接口地址为/api/m3u8?key=kll323&url=,由于没设置ip白名单,则任何人都能使用该接口
y$kll323

#该配置为切片广告功能无效,即不播放切片广告,接口地址为/api/m3u8?key=ksi44ks&url=,由于没设置ip白名单,则任何人都能使用该接口
n$ksi44ks

#该配置为切片广告功能无效,即不播放切片广告,接口地址为/api/m3u8?key=z8kjs&url=,由于设置ip白名单,则只有178.23.4.12能使用该接口
n$z8kjs$178.23.4.12

#我需要上面3个接口同时使用,则把上面3个配置使用英文冒号:分开,再填入设置即可
y$kll323:n$ksi44ks:n$z8kjs$178.23.4.12

且接口不限制数量,可自行设置无数个,且接口默认永久生效,如果你想加个有效时间,则参考:

#过期时间在y/n后面添加,格式年-月-日,如2024-10-23,即超过2024-10-23后,接口就用不了了
y2024-10-23$kll323
n2024-10-23$ksi44ks
n2024-10-23$z8kjs$178.23.4.12
y2024-10-23$kll323:n2024-10-23$ksi44ks:n2024-10-23$z8kjs$178.23.4.12

设置好了后,就可直接通过接口获取可访问的m3u8链接了,如:

#直接请求 解析接口 + m3u8链接 就可以获取了,且同时适用于自适应M3U8和分辨率M3U8
http://127.0.0.1:3000/api/m3u8?key=kll323&url=http://127.0.0.1:3000/movie/auto/61f68205eeeec878ae3fe3d5.m3u8

#返回字段
{
    "code": 200,
    "url": "http://127.0.0.1:3000/movie/auto/61f68205eeeec878ae3fe3d5.m3u8?counts=2&timestamp=1717515649572&key=c67058e8c650e6136dda594f46d45265&a=y",
    "expirytime": "2024-08-14 11:40:49",
    "requestlimit": 2
}

url为获取的可使用的m3u8链接,expirytime为链接过期时间,默认2分钟后过期,requestlimit为链接读取限制次数。

可自行编辑controller/cms.js,搜索expirytime关键词,定位到相关地方修改过期时间和次数,都有注释,然后使用pm2 reload efvcms重启生效。

自适应M3U8对接步骤

首先点击EFV后台左侧 ⇒ 转码相关 ⇒ 防盗链/图片加密设置,开启时间戳+播放次数防盗链并设置KEY值,且值为efvtoken

比如我想播放如下链接:

http://127.0.0.1:3000/movie/auto/61f68205eeeec878ae3fe3d5.m3u8

这个链接不需要域名及/movies及后缀.m3u8,只需要这个/auto开始的部分链接,比如:

/auto/61f68205eeeec878ae3fe3d5

然后拼接字符串,拼接如下:

#拼接后的字符串
/auto/61f68205eeeec878ae3fe3d5&counts=2&timestamp=1613982600000efvtoken

#拼接参数
counts为请求多少次就过期
timestamp为过期的13位时间戳+设置的key值,比如现在16:50:16,10分钟后过期,就是17:00:16,就把17:00:16转化为13位时间戳

接下来使用md5加密该字符串,生成32位小写,然后拿到这个32位小写027e1575ff97deaad7cfe223b6cf6001

最后获取能播放的url链接如下:

http://127.0.0.1:3000/movie/auto/61f68205eeeec878ae3fe3d5.m3u8?counts=2&timestamp=1613982600000&key=027e1575ff97deaad7cfe223b6cf6001

注意最后的url链接counts和拼接的值一样;timestamp只要时间戳,后面不需要加设置的KEY值;key值为拼接字符串md5加密的值。

且拼接加密的时候counts前面用的&,而最后的url链接counts前面是?,一定要区分清楚,不然对接会播放不了。

JS演示示例

const crypto = require('crypto');

function getKey(url, tokenKey) {
const nowstamp = Date.now();
const dutestamp = nowstamp * 1 + 60 * 1000 * 60;
const newurl = url.replace('/movie', '').replace('.m3u8', '');
const tokenurl = newurl + '&counts=' + 2 + '&timestamp=' + dutestamp + tokenKey;
const md5 = crypto.createHash('md5');
const md5token = md5.update(tokenurl).digest('hex');
const outurl = '/movie' + newurl + '.m3u8' + '?counts=' + 2 + '&timestamp=' + dutestamp + '&key=' + md5token;
console.log(outurl);
}

//2个参数为视频自适应M3U8链接和时间戳KEY值
getKey("/movie/auto/61f68205eeeec878ae3fe3d5.m3u8", "efvtoken");

分辨率M3U8对接步骤

首先点击EFV后台左侧 ⇒ 转码相关 ⇒ 防盗链/图片加密设置,开启时间戳+播放次数防盗链并设置KEY值,且值为efvtoken

比如我想播放如下链接:

http://127.0.0.1:3000/videos/202201/31/61f68205eeeec878ae3fe3d5/217g7g/index.m3u8

这个链接不需要域名,只需要/videos开头到m3u8结尾的链接,比如:

/videos/202201/31/61f68205eeeec878ae3fe3d5/217g7g/index.m3u8

然后拼接字符串,拼接如下:

#拼接后的字符串
/videos/202201/31/61f68205eeeec878ae3fe3d5/217g7g/index.m3u8&counts=2&timestamp=1613982600000efvtoken

#拼接参数
counts为请求多少次就过期
timestamp为过期的13位时间戳+设置的key值,比如现在16:50:16,10分钟后过期,就是17:00:16,就把17:00:16转化为13位时间戳

接下来使用md5加密该字符串,生成32位小写,然后拿到这个32位小写027e1575ff97deaad7cfe223b6cf6001

最后获取能播放的url链接如下:

http://127.0.0.1:3000/videos/202201/31/61f68205eeeec878ae3fe3d5/217g7g/index.m3u8?counts=2&timestamp=1613982600000&key=027e1575ff97deaad7cfe223b6cf6001

注意最后的url链接counts和拼接的值一样;timestamp只要时间戳,后面不需要加设置的KEY值;key值为拼接字符串md5加密的值。

且拼接加密的时候counts前面用的&,而最后的url链接counts前面是?,一定要区分清楚,不然对接会播放不了。

JS演示示例

const crypto = require('crypto');

function getKey(url, tokenKey) {
const nowstamp = Date.now();
const dutestamp = nowstamp * 1 + 60 * 1000 * 60;
const tokenurl = url + '&counts=' + 2 + '&timestamp=' + dutestamp + tokenKey;
const md5 = crypto.createHash('md5');
const md5token = md5.update(tokenurl).digest('hex');
const outurl = url + '?counts=' + 2 + '&timestamp=' + dutestamp + '&key=' + md5token;
console.log(outurl);
}

//2个参数为分辨率M3U8链接和时间戳KEY值
getKey("/videos/202201/31/61f68205eeeec878ae3fe3d5/217g7g/index.m3u8", "efvtoken");

相关提示

强烈建议切片开启TS加密功能,可在转码设置开启该功能,开启后切片的key文件会自动加入到时间戳验证环节,从而时间戳会更有效。

且时间戳间隔可以尽量改短点,比如5s10s,只需要让用户播放获取到m3u8后几秒就失效,根据自己服务器网络读取速度决定。

counts次数最少设置为2,或者3次,有些手机浏览器会替换播放器,导致需要多加载1次,甚至2次才能播放,自行调整即可。

6 - HLS双重防盗链对接教程

使用该防盗链之前,必须开启TS加密功能,否则会导致M3U8验证出问题,最好刚安装的时候,就开启TS加密。

对接之前请先了解下HLS双重防盗链的原理及设置 ⇒ 传送门,目前提供2种方案:

1、绑定域名+绑定防盗链Key,绑定域名后,只有该域名能使用专用的hls.js文件,不经常换域名的可以使用该方案。
2、不绑定域名+绑定防盗链Key,经常换域名的可以使用该方案。

根据自身需求选择合适的方案即可。

获取HLS解密代码

先点击EFV后台左侧 ⇒ 转码相关 ⇒ 防盗链/图片加密设置,设置HLS双重防盗链KEY,如efvtoken,然后根据要求自己编辑相关代码。

绑定域名代码

"use strict";
r.d(t, "a", (function() {
return s
})),
r.d(t, "b", (function() {
return o
}));
var i = r(7),
a = "efv",  //HLS双重防盗链key值前半部分
n = "token";  //HLS双重防盗链key值后半部分
function s(e) {
if ("" === e || null == e) return "";
var t = i.enc.Utf8.parse("efvtoken");  //HLS双重防盗链key值
return "string" != typeof e && (e = e.toString()),
i.DES.decrypt({
    ciphertext: i.enc.Base64.parse(e)
},
t, {
    mode: i.mode.ECB,
    padding: i.pad.Pkcs7
}).toString(i.enc.Utf8)
}
function o(e) {
if ("" === e || null == e) return "";
var chars = '4950554648464846495852484848';
var hosts = location.host;
var encodestring = '';
if(hosts != 'play.efvcms.com') {  //绑定域名,不需要http的头,直接填域名就可以了
    throw new URIError('绑定域名错误!');
}
var t = a + n;
t = t.substr(0, 4) + t;
var r = i.enc.Utf8.parse(t);
return "string" != typeof e && (e = e.toString()),
i.DES.decrypt({
    ciphertext: i.enc.Base64.parse(e)
},
r, {
    mode: i.mode.ECB,
    padding: i.pad.Pkcs7
}).toString(i.enc.Utf8)
}

不绑定域名代码

"use strict";
r.d(t, "a", (function() {
return s
})),
r.d(t, "b", (function() {
return o
}));
var i = r(7),
a = "efv",  //HLS双重防盗链key值前半部分
n = "token";  //HLS双重防盗链key值后半部分
function s(e) {
if ("" === e || null == e) return "";
var t = i.enc.Utf8.parse("efvtoken");  //HLS双重防盗链key值
return "string" != typeof e && (e = e.toString()),
i.DES.decrypt({
    ciphertext: i.enc.Base64.parse(e)
},
t, {
    mode: i.mode.ECB,
    padding: i.pad.Pkcs7
}).toString(i.enc.Utf8)
}
function o(e) {
if ("" === e || null == e) return "";
var chars = '4950554648464846495852484848';
var encodestring = '';
var t = a + n;
t = t.substr(0, 4) + t;
var r = i.enc.Utf8.parse(t);
return "string" != typeof e && (e = e.toString()),
i.DES.decrypt({
    ciphertext: i.enc.Base64.parse(e)
},
r, {
    mode: i.mode.ECB,
    padding: i.pad.Pkcs7
}).toString(i.enc.Utf8)
}

加密HLS解密代码

先前往JS加密网站 ⇒ 传送门,再调整加密选项,Options Preset选择LowDebug Protection打上勾,也就是禁止浏览器调试,其它全部默认即可。

再将上面修改好的解密代码复制到该网站进行加密,会获取类似var _0x487f6e=开头的一串加密代码,然后下载hls.js文件 ⇒ 传送门

再编辑里面的hls.js文件,修改如下:

#搜索hls-anti-start关键词,然后将上面获取到的加密代码丢到中间
function(e, t, r) {
//hls-anti-start
var _0x487f6e=_0x1ec8;(function(_0x1fbff6,_0x8647ea)...
//hls-anti-end
},

这时候我们就获取了加密解密代码的完整hls.js文件,接下来还需要压缩一下该js文件。

压缩HLS.JS文件

这里使用google的压缩网站 ⇒ 传送门,如果你访问不了,可以自己找下其它JS压缩网站,自行压缩。

最终我们获取到了压缩并加密后的完整hls.js文件。

使用HLS.JS文件

测试HLS.JS文件

使用之前我们先测试一下hls.js文件,这里提供一个Dplayer单页面html文件 ⇒ 传送门,将hls.js放到解压后的dplayer文件夹,再修改dplayer.html文件,编辑一下里面的m3u8地址为你自己的。

如果你的hls.js文件没有绑定域名,则直接在本地电脑使用Google Chrome浏览器双击打开dplayer.html文件就可以测试播放了。

如果你的hls.js文件绑定了域名,就需要将整个dplayer文件夹传到你绑定域名的根目录,然后直接访问/dplayer/dplayer.html路径就可以播放了。

使用HLS.JS文件

我们测试没问题后,再去自己的网站,找到网站播放器用的hls.js文件或hls.min.js文件,然后将解密的替换hls.js文件替换进去即可,如果是hls.min.js文件还需要改名覆盖。

特别提示

要想完整使用该功能,需要屏蔽下国产浏览器,这里推荐一个简单的判断国产手机浏览器方法,并引导他们使用谷歌等手机浏览器打开播放。

就是通过Nginx判断UA跳转到指定页面,在前端站点的域名配置文件中添加以下代码:

 if ($http_user_agent ~* "UCBrowser|MQQBrowser|Quark|MicroMessenger") {
  rewrite ^/(.*) /tips.html break;
  }

这里主要争对拥有最大市场的QQUC夸克微信做跳转,判断后,会跳转到站点根目录的tips.html提示页面。

不会自己写提示页面的,这里提供一个参考,在站点根目录新建一个tips.html文件,内容如下:

 <!doctype html>
 <html>
 <head>
     <meta charset="utf-8">
     <title>特别提示!</title>
     <style>
         .container {
             width: 60%;
             margin: 10% auto 0;
             background-color: #f0f0f0;
             padding: 2% 5%;
             border-radius: 10px
         }
 
         ul {
             padding-left: 20px;
         }

             ul li {
                 line-height: 2.3
             }

         a {
             color: #20a53a
         }
     </style>
 </head>
 <body>
     <div class="container">
         <h3>本站已禁止在该手机浏览器上打开。</h3>
         <ul>
             <li>建议使用谷歌手机浏览器打开本站点,可让您得到最佳体验。</li>
             <li>谷歌手机浏览器:<a href="https://www.efvcms.com/" target="_blank">点击下载</a></li>
         </ul>
     </div>
 </body>
 </html>

提示内容请自行更改,浏览器下载地址自行替换。

当然还有部分可以劫持播放器的手机浏览器没列举,不懂的可以百度/谷歌下你要添加的手机浏览器UA,找到专属标识英文,自行添加到配置文件中即可。

7 - 封面/截图加密对接教程

开启图片加密后,基本上EFV生成的封面、截图全部会被加密乱码,完全看不到任何实质内容,需前端解密调用。

对接之前请先了解下封面截图加密的原理及设置 ⇒ 传送门,这里只提供一个前端html使用示例,然后自行对接。

HTML示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>EFV加密图片解密展示</title>
</head>
<body>
<img id="decodedImage" src="" alt="解密后的图片将显示在这里">

<script>
function fetchEncryptedImage() {
    return fetch('http://127.0.0.1:3000/videos/202403/08/65eb06d5561d9e3d0b990740/2.jpg').then(response => response.arrayBuffer());
}

// 解密函数,对图片数据的前9个字节进行XOR操作
function decryptImage(data) {
    let view = new Uint8Array(data);
    for (let i = 0; i < Math.min(9, view.length); i++) {
        view[i] = view[i] ^ 0x12; // 使用0x12作为密钥
    }
    return data;
}

// 将解密后的图片数据设置为img元素的src属性
function displayDecryptedImage(data) {
    let blob = new Blob([data]);
    let url = URL.createObjectURL(blob);
    document.getElementById('decodedImage').src = url;
}

// 加载并解密图片
fetchEncryptedImage().then(encryptedData => {
    let decryptedData = decryptImage(encryptedData);
    displayDecryptedImage(decryptedData);
}).catch(error => console.error('加载或解密图片时出错:', error));
</script>
</body>
</html>

8 - 封面/截图文本对接教程

开启图片转换文本后,EFV生成的封面、截图链接无法正常显示,且会生成对应的txt文本链接,需前端解密调用。

对接之前请先了解下封面截图转文本的原理及设置 ⇒ 传送门,这里只提供一个前端html使用示例,然后自行对接。

HTML示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>EFV文本图片展示</title>
</head>
<body>
<img id="base64Image" src="" alt="从链接解密Base64后的图片将显示在这里">

<script>
let base64TextUrl = "https://demo.zhuanma.co/videos/202403/14/65f2f08685a309da271d0dec/cover.txt";

// 使用fetch API从链接获取Base64文本
fetch(base64TextUrl)
.then(response => response.text()) // 获取文本内容
.then(base64Text => {
    let imageDataUrl = "data:image/png;base64," + base64Text;
    // 设置图片的src属性为DataURL
    document.getElementById('base64Image').src = imageDataUrl;
})
.catch(error => console.error('获取Base64文本失败:', error));
</script>
</body>
</html>

9 - 全闭环转码使用参考

我们在EFV设置好相关类型水印、跑马灯等选项后,可以直接远程使用API上传、下载、入库并自动获取M3U8等信息。

全闭环转码系统,即搭建好EFV系统后,设置好相关的转码参数,然后可以不用管了,这时候可以在任意服务器或者第三方程序上利用EFV高级版API各种调用。

使用api前,请前往转码设置,开启api,然后修改下apikey等各种参数,尽量复杂点,且自动转码队列需要开启,这里只列举3个方案。

方案一:远程上传视频,然后自动转码后,EFV自动将M3U8等链接信息POST到通知接口,再获取入库,所需接口 ⇒ 上传API通知接口获取EFV运行状态删除视频

方案二:远程创建转码任务,然后创建完成并自动转码后,EFV自动将M3U8等链接信息POST到通知接口,再获取入库,所需接口 ⇒ 创建转码任务通知接口获取EFV运行状态删除视频

方案三:使用下载中心,远程下载M3U8MP4YOUTUBE等直链视频,下载完成并自动转码后,EFV自动将M3U8等链接信息POST到通知接口,再获取入库,所需接口 ⇒ 下载API通知接口获取EFV运行状态删除视频

还有更多方案可直接查看API文档自行整合使用。