logo NodeSeekbeta

【教程】cloudflare优选IP反代emby

12345
  • 通用反代的代码

    使用方法
    访问时,只需在对应入口后拼接目标地址即可使用。

    方式一:拼接完整 URL
    示例:
    https://yx.emby-ct.ccwu.cc:443/https://emby.com

    方式二:直接拼接域名
    示例:
    https://yx.emby-ct.ccwu.cc:443/emby.com

    /**
     * ============================================================
     * Emby / Jellyfin Worker v4.5 Final
     *
     * 功能:
     * 1. 动态反向代理
     * 2. Emby / Jellyfin 播放优化
     * 3. 视频流不缓存
     * 4. 静态资源缓存
     * 5. Range断点播放
     * 6. WebSocket支持
     * 7. 防止源站跳转泄露
     *
     * 支持:
     * /https://example.com
     * /example.com
     * /example.com/Users/xxx
     *
     * ============================================================
     */
    
    
    addEventListener(
    "fetch",
    e=>e.respondWith(proxy(e.request,e))
    );
    
    
    
    async function proxy(req,event){
    
    // =============================
    // 基础信息
    // =============================
    
    const url=new URL(req.url);
    
    const country=
    req.cf?.country||
    req.headers.get("cf-ipcountry");
    
    
    // =============================
    // 中国大陆限制
    // =============================
    
    if(
    country &&
    country!=="CN"
    ){
    
    return tip(
    "🌏 访问限制",
    "当前服务仅允许中国大陆访问"
    );
    
    }
    
    
    // =============================
    // 解析目标服务器
    // =============================
    
    const target=
    parseTarget(
    url.pathname.slice(1)
    );
    
    
    if(!target){
    
    return tip(
    "⚠️ 地址错误",
    "请添加Emby服务器地址\n\n示例:\n/https://emby.example.com"
    );
    
    }
    
    
    
    const dest=
    new URL(
    target.base+
    target.path+
    url.search
    );
    
    
    
    // =============================
    // 请求头处理
    // =============================
    
    const headers=
    new Headers(req.headers);
    
    
    // 必须修改Host
    // 否则部分Nginx/Caddy/宝塔会拒绝
    
    headers.set(
    "Host",
    dest.host
    );
    
    
    // 删除可能冲突的CF头
    
    headers.delete(
    "CF-Ray"
    );
    
    headers.delete(
    "CF-IPCountry"
    );
    
    headers.delete(
    "CF-Visitor"
    );
    
    
    // 保留真实客户端IP
    
    const ip=
    req.headers.get(
    "CF-Connecting-IP"
    );
    
    
    if(ip){
    
    headers.set(
    "CF-Connecting-IP",
    ip
    );
    
    headers.set(
    "X-Forwarded-For",
    ip
    );
    
    headers.set(
    "X-Real-IP",
    ip
    );
    
    }
    
    
    headers.set(
    "X-Forwarded-Proto",
    "https"
    );
    
    
    
    // =============================
    // Range播放优化
    // =============================
    
    if(
    req.headers.has("Range")
    ){
    
    headers.set(
    "Accept-Encoding",
    "identity"
    );
    
    
    headers.delete(
    "If-Range"
    );
    
    }
    
    
    
    
    try{
    
    
    // =============================
    // WebSocket支持
    // =============================
    
    if(
    req.headers
    .get("upgrade")
    ?.toLowerCase()
    ==="websocket"
    ){
    
    return fetch(
    new Request(
    dest,
    {
    method:req.method,
    headers,
    body:req.body,
    redirect:"manual"
    }
    )
    );
    
    }
    
    
    
    
    // =============================
    // 请求源站
    // =============================
    
    const res=
    await fetch(
    new Request(
    dest,
    {
    method:req.method,
    headers,
    
    body:
    req.method==="GET"||
    req.method==="HEAD"
    ?
    undefined
    :
    req.body,
    
    redirect:"manual"
    }
    )
    );
    
    
    
    
    // =============================
    // 重定向处理
    //
    // 防止源站域名暴露
    //
    // 例如:
    // Location:
    // https://emby.com/web
    //
    // 改成:
    // /https%3A%2F%2Femby.com%2Fweb
    // =============================
    
    if(
    [
    301,
    302,
    303,
    307,
    308
    ]
    .includes(res.status)
    ){
    
    const h=
    new Headers(res.headers);
    
    
    const loc=
    h.get("location");
    
    
    if(
    loc &&
    /^https?:\/\//i.test(loc)
    ){
    
    h.set(
    "location",
    "/"+
    encodeURIComponent(loc)
    );
    
    }
    
    
    return new Response(
    null,
    {
    status:res.status,
    headers:h
    }
    );
    
    }
    
    
    
    
    const type=
    res.headers.get(
    "content-type"
    )||"";
    
    
    
    
    // =============================
    // 视频流处理
    //
    // 视频:
    // mp4
    // mkv
    // m3u8
    // ts
    // mpd
    //
    // 永不缓存
    // =============================
    
    if(
    isVideo(target.path)||
    type.startsWith("video/")
    ){
    
    const h=
    new Headers(res.headers);
    
    
    h.set(
    "Cache-Control",
    "no-store,no-cache,must-revalidate"
    );
    
    
    h.set(
    "Accept-Ranges",
    "bytes"
    );
    
    
    return new Response(
    res.body,
    {
    status:res.status,
    headers:h
    }
    );
    
    }
    
    
    
    
    // =============================
    // 静态资源缓存
    //
    // 图片:
    // jpg/png/webp
    //
    // 前端:
    // js/css
    //
    // 字体:
    // woff
    // =============================
    
    if(
    canCache(
    target.path,
    type
    )
    ){
    
    const cache=
    caches.default;
    
    
    
    const key=
    new Request(
    url.origin+
    target.base+
    target.path
    );
    
    
    
    const hit=
    await cache.match(key);
    
    
    if(hit)
    return hit;
    
    
    
    
    const h=
    new Headers(res.headers);
    
    
    h.set(
    "Cache-Control",
    "public,max-age=86400"
    );
    
    
    
    const result=
    new Response(
    res.body,
    {
    status:res.status,
    headers:h
    }
    );
    
    
    
    event.waitUntil(
    cache.put(
    key,
    result.clone()
    )
    );
    
    
    
    return result;
    
    }
    
    
    
    
    // =============================
    // API默认不缓存
    // =============================
    
    const h=
    new Headers(res.headers);
    
    
    h.set(
    "Cache-Control",
    "no-store"
    );
    
    
    
    return new Response(
    res.body,
    {
    status:res.status,
    headers:h
    }
    );
    
    
    
    }catch(e){
    
    
    return tip(
    "❌ Emby连接失败",
    `无法连接源站:
    
    ${dest.hostname}
    
    可能原因:
    1. 源站离线
    2. DNS异常
    3. 防火墙限制`
    );
    
    
    }
    
    }
    
    
    
    
    
    
    
    // ============================================================
    // 地址解析
    // ============================================================
    
    
    function parseTarget(str){
    
    if(!str)
    return null;
    
    
    try{
    
    str=
    decodeURIComponent(str);
    
    }catch(e){}
    
    
    
    if(
    str.startsWith("http://")||
    str.startsWith("https://")
    ){
    
    const m=
    str.match(
    /^(https?:\/\/[^\/]+)(\/.*)?$/
    );
    
    
    return m?
    {
    base:m[1],
    path:m[2]||""
    }
    :
    null;
    
    }
    
    
    
    const m=
    str.match(
    /^([^\/]+)(\/.*)?$/
    );
    
    
    
    return m?
    {
    base:normalize(m[1]),
    path:m[2]||""
    }
    :
    null;
    
    }
    
    
    
    
    
    
    // ============================================================
    // 自动判断协议
    // ============================================================
    
    
    function normalize(host){
    
    const port=
    host.match(/:(\d+)$/)
    ?.[1];
    
    
    if(
    port==="443"||
    port==="8920"||
    port==="8443"
    ){
    
    return "https://"+host;
    
    }
    
    
    
    if(port){
    
    return "http://"+host;
    
    }
    
    
    
    return "https://"+host;
    
    }
    
    
    
    
    
    
    // ============================================================
    // 获取文件后缀
    // ============================================================
    
    
    function ext(path){
    
    return path
    .split("?")[0]
    .split(".")
    .pop()
    .toLowerCase();
    
    }
    
    
    
    
    
    
    // ============================================================
    // 视频类型判断
    // ============================================================
    
    
    function isVideo(path){
    
    return [
    
    "mp4",
    "mkv",
    "avi",
    "mov",
    "m4v",
    "ts",
    "m3u8",
    "mpd",
    "webm"
    
    ]
    .includes(
    ext(path)
    );
    
    }
    
    
    
    
    
    
    
    // ============================================================
    // 静态缓存判断
    // ============================================================
    
    
    function canCache(path,type){
    
    const e=
    ext(path);
    
    
    return [
    
    "jpg",
    "jpeg",
    "png",
    "gif",
    "webp",
    "svg",
    "ico",
    
    "woff",
    "woff2",
    "ttf",
    
    "css",
    "js"
    
    ]
    .includes(e)
    
    ||
    
    type.startsWith("image/")
    
    ||
    
    type.includes("javascript")
    
    ||
    
    type.includes("css");
    
    }
    
    
    
    
    
    
    // ============================================================
    // 错误页面
    // ============================================================
    
    
    function tip(title,msg){
    
    return new Response(
    
    `${title}
    
    ${msg}
    
    ----------------
    Emby Worker v4.5
    `,
    
    {
    status:400,
    headers:{
    "Content-Type":
    "text/plain;charset=utf-8",
    
    "Cache-Control":
    "no-store"
    }
    }
    
    );
    
    }
    
    
  • 403错误

  • @yl0919 #44 把域名设置直连,不要走代理

12345

你好啊,陌生人!

我的朋友,看起来你是新来的,如果想参与到讨论中,点击下面的按钮!

📈用户数目📈

目前论坛共有63962位seeker

🎉欢迎新用户🎉