GithubHelp home page GithubHelp logo

onelist's People

Contributors

ddsderek avatar ddsrem avatar msterzhang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

onelist's Issues

无法播放

能刮削,但是播放不了,F12看到的视频文件地址,拷贝出来直接访问能迅雷下载。

用网页播放不了视频

不知道为什么不能浏览器网页在线播放(Chrome、Safari、edge浏览器都不行),一直重新连接,但是用app比如inna打开就能播放。

许多搜刮不到

可以借鉴nastools代码,用软连接搜刮,这样子就不影响原文件,而且可以自动改名搜刮。nastools改名搜刮部分优化了很多次,已经可以搜刮到大多数不同的命名格式。
思路:在数据库中新建一张表,用来记录,改名文件在alist中的链接与改过的名字的对应关系。
不建议直接在alist改名。
1.aliist现在用的阿里云盘 Open在同一 IP 在120分钟内请求10次,会出现 TooManyRequests
(例如在 保存/编辑 的时候算一次请求,查看文件看视频下载不算),如果在alist改,容易触发阿里云盘的风控
2.这样更灵活,可以用第三方的手动改名软件本地改名。

功能建议

增加一个自定义tmdbapi地址
可以使用clodflare反代tmdb api
下面这个代码可以反代api.themoviedb.org

// node_modules/reflare/dist/src/database/workers-kv.js
var WorkersKV = class {
  namespace;
  constructor(namespace) {
    this.namespace = namespace;
  }
  get = async (key) => {
    const value = await this.namespace.get(key, {
      type: "json",
      cacheTtl: 60
    });
    return value;
  };
  put = async (key, value) => {
    await this.namespace.put(key, JSON.stringify(value));
  };
  delete = async (key) => {
    await this.namespace.delete(key);
  };
};

// node_modules/reflare/dist/src/middleware.js
var usePipeline = (...initMiddlewares) => {
  const stack = [...initMiddlewares];
  const push = (...middlewares) => {
    stack.push(...middlewares);
  };
  const execute = async (context) => {
    const runner = async (prevIndex, index) => {
      if (index === prevIndex) {
        throw new Error("next() called multiple times");
      }
      if (index >= stack.length) {
        return;
      }
      const middleware = stack[index];
      const next = async () => runner(index, index + 1);
      await middleware(context, next);
    };
    await runner(-1, 0);
  };
  return {
    push,
    execute
  };
};

// node_modules/reflare/dist/src/middlewares/cors.js
var useCORS = async (context, next) => {
  await next();
  const { request, response, route } = context;
  const corsOptions = route.cors;
  if (corsOptions === void 0) {
    return;
  }
  const { origin, methods, exposedHeaders, allowedHeaders, credentials, maxAge } = corsOptions;
  const requestOrigin = request.headers.get("origin");
  if (requestOrigin === null || origin === false) {
    return;
  }
  const corsHeaders = new Headers(response.headers);
  if (origin === true) {
    corsHeaders.set("Access-Control-Allow-Origin", requestOrigin);
  } else if (Array.isArray(origin)) {
    if (origin.includes(requestOrigin)) {
      corsHeaders.set("Access-Control-Allow-Origin", requestOrigin);
    }
  } else if (origin === "*") {
    corsHeaders.set("Access-Control-Allow-Origin", "*");
  }
  if (Array.isArray(methods)) {
    corsHeaders.set("Access-Control-Allow-Methods", methods.join(","));
  } else if (methods === "*") {
    corsHeaders.set("Access-Control-Allow-Methods", "*");
  } else {
    const requestMethod = request.headers.get("Access-Control-Request-Method");
    if (requestMethod !== null) {
      corsHeaders.set("Access-Control-Allow-Methods", requestMethod);
    }
  }
  if (Array.isArray(exposedHeaders)) {
    corsHeaders.set("Access-Control-Expose-Headers", exposedHeaders.join(","));
  } else if (exposedHeaders === "*") {
    corsHeaders.set("Access-Control-Expose-Headers", "*");
  }
  if (Array.isArray(allowedHeaders)) {
    corsHeaders.set("Access-Control-Allow-Headers", allowedHeaders.join(","));
  } else if (allowedHeaders === "*") {
    corsHeaders.set("Access-Control-Allow-Headers", "*");
  } else {
    const requestHeaders = request.headers.get("Access-Control-Request-Headers");
    if (requestHeaders !== null) {
      corsHeaders.set("Access-Control-Allow-Headers", requestHeaders);
    }
  }
  if (credentials === true) {
    corsHeaders.set("Access-Control-Allow-Credentials", "true");
  }
  if (maxAge !== void 0 && Number.isInteger(maxAge)) {
    corsHeaders.set("Access-Control-Max-Age", maxAge.toString());
  }
  context.response = new Response(response.body, {
    status: response.status,
    statusText: response.statusText,
    headers: corsHeaders
  });
};

// node_modules/reflare/dist/src/middlewares/firewall.js
var fields = /* @__PURE__ */ new Set([
  "country",
  "continent",
  "asn",
  "ip",
  "hostname",
  "user-agent"
]);
var operators = /* @__PURE__ */ new Set([
  "equal",
  "not equal",
  "greater",
  "less",
  "in",
  "not in",
  "contain",
  "not contain",
  "match",
  "not match"
]);
var validateFirewall = ({ field, operator, value }) => {
  if (field === void 0 || operator === void 0 || value === void 0) {
    throw new Error("Invalid 'firewall' field in the option object");
  }
  if (fields.has(field) === false) {
    throw new Error("Invalid 'firewall' field in the option object");
  }
  if (operators.has(operator) === false) {
    throw new Error("Invalid 'firewall' field in the option object");
  }
};
var getFieldParam = (request, field) => {
  const cfProperties = request.cf;
  switch (field) {
    case "asn":
      return cfProperties?.asn;
    case "continent":
      return cfProperties?.continent;
    case "country":
      return cfProperties?.country;
    case "hostname":
      return request.headers.get("host") || "";
    case "ip":
      return request.headers.get("cf-connecting-ip") || "";
    case "user-agent":
      return request.headers.get("user-agent") || "";
    default:
      return void 0;
  }
};
var matchOperator = (fieldParam, value) => {
  if (!(value instanceof RegExp)) {
    throw new Error("You must use 'new RegExp('...')' for 'value' in firewall configuration to use 'match' or 'not match' operator");
  }
  return value.test(fieldParam.toString());
};
var notMatchOperator = (fieldParam, value) => !matchOperator(fieldParam, value);
var equalOperator = (fieldParam, value) => fieldParam === value;
var notEqualOperator = (fieldParam, value) => fieldParam !== value;
var greaterOperator = (fieldParam, value) => {
  if (typeof fieldParam !== "number" || typeof value !== "number") {
    throw new Error("You must use number for 'value' in firewall configuration to use 'greater' or 'less' operator");
  }
  return fieldParam > value;
};
var lessOperator = (fieldParam, value) => {
  if (typeof fieldParam !== "number" || typeof value !== "number") {
    throw new Error("You must use number for 'value' in firewall configuration to use 'greater' or 'less' operator");
  }
  return fieldParam < value;
};
var containOperator = (fieldParam, value) => {
  if (typeof fieldParam !== "string" || typeof value !== "string") {
    throw new Error("You must use string for 'value' in firewall configuration to use 'contain' or 'not contain' operator");
  }
  return fieldParam.includes(value);
};
var notContainOperator = (fieldParam, value) => !containOperator(fieldParam, value);
var inOperator = (fieldParam, value) => {
  if (!Array.isArray(value)) {
    throw new Error("You must use an Array for 'value' in firewall configuration to use 'in' or 'not in' operator");
  }
  return value.some((item) => item === fieldParam);
};
var notInOperator = (fieldParam, value) => !inOperator(fieldParam, value);
var operatorsMap = {
  match: matchOperator,
  contain: containOperator,
  equal: equalOperator,
  in: inOperator,
  greater: greaterOperator,
  less: lessOperator,
  "not match": notMatchOperator,
  "not contain": notContainOperator,
  "not equal": notEqualOperator,
  "not in": notInOperator
};
var useFirewall = async (context, next) => {
  const { request, route } = context;
  if (route.firewall === void 0) {
    await next();
    return;
  }
  route.firewall.forEach(validateFirewall);
  for (const { field, operator, value } of route.firewall) {
    const fieldParam = getFieldParam(request, field);
    if (fieldParam !== void 0 && operatorsMap[operator](fieldParam, value)) {
      throw new Error("You don't have permission to access this service.");
    }
  }
  await next();
};

// node_modules/reflare/dist/src/middlewares/headers.js
var setForwardedHeaders = (headers) => {
  headers.set("X-Forwarded-Proto", "https");
  const host = headers.get("Host");
  if (host !== null) {
    headers.set("X-Forwarded-Host", host);
  }
  const ip = headers.get("cf-connecting-ip");
  const forwardedForHeader = headers.get("X-Forwarded-For");
  if (ip !== null && forwardedForHeader === null) {
    headers.set("X-Forwarded-For", ip);
  }
};
var useHeaders = async (context, next) => {
  const { request, route } = context;
  const requestHeaders = new Headers(request.headers);
  setForwardedHeaders(requestHeaders);
  if (route.headers === void 0) {
    context.request = new Request(request.url, {
      body: request.body,
      method: request.method,
      headers: requestHeaders
    });
    await next();
    return;
  }
  if (route.headers.request !== void 0) {
    for (const [key, value] of Object.entries(route.headers.request)) {
      if (value.length === 0) {
        requestHeaders.delete(key);
      } else {
        requestHeaders.set(key, value);
      }
    }
  }
  context.request = new Request(request.url, {
    body: request.body,
    method: request.method,
    headers: requestHeaders
  });
  await next();
  const { response } = context;
  const responseHeaders = new Headers(response.headers);
  if (route.headers.response !== void 0) {
    for (const [key, value] of Object.entries(route.headers.response)) {
      if (value.length === 0) {
        responseHeaders.delete(key);
      } else {
        responseHeaders.set(key, value);
      }
    }
  }
  context.response = new Response(response.body, {
    status: response.status,
    statusText: response.statusText,
    headers: responseHeaders
  });
};

// node_modules/reflare/dist/src/utils.js
var getHostname = (request) => {
  const url = new URL(request.url);
  return url.host;
};
var castToIterable = (value) => Array.isArray(value) ? value : [value];

// node_modules/reflare/dist/src/middlewares/load-balancing.js
var validateUpstream = (upstream) => {
  if (upstream.domain === void 0) {
    throw new Error("Invalid 'upstream' field in the option object");
  }
};
var ipHashHandler = (upstream, request) => {
  const ipString = request.headers.get("cf-connecting-ip") || "0.0.0.0";
  const userIP = ipString.split(".").map((octet, index, array) => parseInt(octet, 10) * 256 ** (array.length - index - 1)).reduce((accumulator, current) => accumulator + current);
  return upstream[userIP % upstream.length];
};
var randomHandler = (upstream) => {
  const weights = upstream.map((option) => option.weight === void 0 ? 1 : option.weight);
  const totalWeight = weights.reduce((acc, num, index) => {
    const sum = acc + num;
    weights[index] = sum;
    return sum;
  });
  if (totalWeight === 0) {
    throw new Error("Total weights should be greater than 0.");
  }
  const random = Math.random() * totalWeight;
  for (const index of weights.keys()) {
    if (weights[index] >= random) {
      return upstream[index];
    }
  }
  return upstream[Math.floor(Math.random() * upstream.length)];
};
var handlersMap = {
  random: randomHandler,
  "ip-hash": ipHashHandler
};
var useLoadBalancing = async (context, next) => {
  const { request, route } = context;
  const { upstream, loadBalancing } = route;
  if (upstream === void 0) {
    throw new Error("The required 'upstream' field in the option object is missing");
  }
  const upstreamIterable = castToIterable(upstream);
  upstreamIterable.forEach(validateUpstream);
  if (loadBalancing === void 0) {
    context.upstream = randomHandler(upstreamIterable, request);
    await next();
    return;
  }
  const policy = loadBalancing.policy || "random";
  const policyHandler = handlersMap[policy];
  context.upstream = policyHandler(upstreamIterable, request);
  await next();
};

// node_modules/reflare/dist/src/middlewares/upstream.js
var rewriteURL = (url, upstream) => {
  const cloneURL = new URL(url);
  const { domain, port, protocol } = upstream;
  cloneURL.hostname = domain;
  if (protocol !== void 0) {
    cloneURL.protocol = `${protocol}:`;
  }
  if (port === void 0) {
    cloneURL.port = "";
  } else {
    cloneURL.port = port.toString();
  }
  return cloneURL.href;
};
var useUpstream = async (context, next) => {
  const { request, upstream } = context;
  if (upstream === null) {
    await next();
    return;
  }
  const url = rewriteURL(request.url, upstream);
  context.request = new Request(url, context.request);
  if (upstream.onRequest) {
    const onRequest = castToIterable(upstream.onRequest);
    context.request = onRequest.reduce((reducedRequest, fn) => fn(reducedRequest, url), request);
  }
  context.response = (await fetch(context.request)).clone();
  if (upstream.onResponse) {
    const onResponse = castToIterable(upstream.onResponse);
    context.response = onResponse.reduce((reducedResponse, fn) => fn(reducedResponse, url), context.response);
  }
  await next();
};

// node_modules/reflare/dist/src/index.js
var filter = (request, routeList) => {
  const url = new URL(request.url);
  for (const route of routeList) {
    if (route.methods === void 0 || route.methods.includes(request.method)) {
      const match = castToIterable(route.path).some((path) => {
        const re = RegExp(`^${path.replace(/(\/?)\*/g, "($1.*)?").replace(/\/$/, "").replace(/:(\w+)(\?)?(\.)?/g, "$2(?<$1>[^/]+)$2$3").replace(/\.(?=[\w(])/, "\\.").replace(/\)\.\?\(([^[]+)\[\^/g, "?)\\.?($1(?<=\\.)[^\\.")}/*$`);
        return url.pathname.match(re);
      });
      if (match) {
        return route;
      }
    }
  }
  return void 0;
};
var defaultOptions = {
  provider: "static",
  routeList: []
};
var useReflare = async (options = defaultOptions) => {
  const pipeline = usePipeline(useFirewall, useLoadBalancing, useHeaders, useCORS, useUpstream);
  const routeList = [];
  if (options.provider === "static") {
    for (const route of options.routeList) {
      routeList.push(route);
    }
  }
  if (options.provider === "kv") {
    const database = new WorkersKV(options.namespace);
    const routeListKV = await database.get("route-list") || [];
    for (const routeKV of routeListKV) {
      routeList.push(routeKV);
    }
  }
  const handle = async (request) => {
    const route = filter(request, routeList);
    if (route === void 0) {
      return new Response("Failed to find a route that matches the path and method of the current request", {
        status: 500
      });
    }
    const context = {
      request: request.clone(),
      route,
      hostname: getHostname(request),
      response: new Response("Unhandled response"),
      upstream: null
    };
    try {
      await pipeline.execute(context);
    } catch (error) {
      if (error instanceof Error) {
        context.response = new Response(error.message, {
          status: 500
        });
      }
    }
    return context.response;
  };
  const unshift = (route) => {
    routeList.unshift(route);
  };
  const push = (route) => {
    routeList.push(route);
  };
  return {
    handle,
    unshift,
    push
  };
};
var src_default = useReflare;

// src/index.ts
var src_default2 = {
  async fetch(request) {
    const reflare = await src_default();
    reflare.push({
      path: "/*",
      upstream: {
        domain: "api.themoviedb.org",
        protocol: "https"
      },
      cors: {
        origin: "*"
      }
    });
    return reflare.handle(request);
  }
};
export {
  src_default2 as default
};
//# sourceMappingURL=index.js.map

签名开启时支持播放

大部分用户是在同一台服务器上挂载alist和onelist,用127.0.0.1访问alist更加安全
若Alist返回302,onelist转发也不会消耗流量

onelist播放视频相对与alist很卡

在Alist的主页里打开的阿里云盘视频可以很顺滑播放,但是在onelist之中的挂载目录中打开同一视频就会很卡,是不是意味着onelist播放的视频受到服务器带宽的限制?

兼容emby客户端么

这样无论哪个平台,安卓或者苹果,电视还是手机都可以用了。谢谢

最新版仍然无法播放

设备: N1,已安装最新版alist,版本号3.14,已禁止签名所有。onelist正常刮削成功。但仍然无法播放,用的是阿里open的链接,已确定文件格式为H264的MP4,所以真的不知道我错在哪里? 莫非是隐私内容正则表达式那里出错?求解

功能建议

能不能搞黄啊挂pikpak搞黄简直太方便了还能海报强。

English language interface

Hi. I just found this project some days ago and I love it but I don't speak Chinese. Do you mind adding an English interface? Or at least add an option to scrape TheMovieDb info in English.
Thank you in advance.

新增挂载时失败

docker部署,提交时提示以下报错信息
context deadline exceeded (Client.Timeout exceeded while awaiting headers)

docker安装报错

manifest for docker.io/msterzhang/onelist:latest not found
是改仓库了吗

使用极空间docker安装启动后,打不开内网ip:5245

安装说明里好像并没有针对极空间的说明,不好意思,我是新手,可以帮忙看一下吗?
2023-07-17T22:26:23.778901922Z s6-rc: info: service s6rc-oneshot-runner: starting 2023-07-17T22:26:23.797422442Z s6-rc: info: service s6rc-oneshot-runner successfully started 2023-07-17T22:26:23.798181090Z s6-rc: info: service fix-attrs: starting 2023-07-17T22:26:23.817622430Z s6-rc: info: service fix-attrs successfully started 2023-07-17T22:26:23.818035734Z s6-rc: info: service legacy-cont-init: starting 2023-07-17T22:26:23.835008624Z cont-init: info: running /etc/cont-init.d/010-config 2023-07-17T22:26:25.122043041Z cont-init: info: /etc/cont-init.d/010-config: 初始化成功! 2023-07-17T22:26:29.986874312Z cont-init: info: /etc/cont-init.d/010-config: 修改完config.env配置文件后,运行onelist -run server便可启动项目,忘记密码运行onelist -run admin可查看管理员账户! 2023-07-17T22:26:29.987037650Z cont-init: info: /etc/cont-init.d/010-config: 2023/07/18 06:26:29 初始化缓存系统成功! 2023-07-17T22:26:29.987076443Z cont-init: info: /etc/cont-init.d/010-config: 账号:[email protected] 密码:xxxxx 2023-07-17T22:26:29.987964012Z cont-init: info: /etc/cont-init.d/010-config exited 0 2023-07-17T22:26:29.990223913Z s6-rc: info: service legacy-cont-init successfully started 2023-07-17T22:26:29.990653843Z s6-rc: info: service legacy-services: starting 2023-07-17T22:26:30.032887874Z services-up: info: copying legacy longrun onelist (no readiness notification) 2023-07-17T22:26:30.057252988Z s6-rc: info: service legacy-services successfully started 2023-07-17T22:26:30.858273866Z 2023/07/18 06:26:30 初始化缓存系统成功!

提个小问题

音轨不能自己选择 我有个remux的资源 播放出来读的是评论音轨

内存占用较高

尤其是扫库的时候,CPU和内存占用相当高。
如果alist资源新增了,onelist是不是还要完整扫一遍?
另外期待新增telegram讨论组。

增强刮削能力

目前非常多电影都不能刮削出来 即使是很规范的命名 其他刮削软件可以秒刮削 onelist. 确不认,
能否兼容其他软件的命名规范

建议增加软链接功能
把alist挂载到本地后,onelist 在其他目录建立软链接, 不对实际文件重命名 而是用新文件名建立原文件的软链接 然后 在软链接目录储存刮削信息

可以参考nastools

最好把nastools的刮削代码都移植过来

媒体库添加挂载目录报错

群晖Docker安装
Alist访问路径有端口号
媒体中心挂载目录,按提示输入/阿里云盘/电视剧
invalid character<`looking for beginning of value
image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.