Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update #183

Merged
merged 1 commit into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 91 additions & 68 deletions repo/api.yinyuetai.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// ==MiruExtension==
// @name 音悦台MTV
// @version v0.0.1
// @version v0.0.2
// @author vvsolo
// @lang zh
// @license MIT
Expand All @@ -12,50 +12,94 @@
// ==/MiruExtension==

export default class extends Extension {
#channelDefault = '6998499728862334976';
#channelList = {
'6998499728862334976': '华语',
'6951459197061984256': '欧美',
'6998475633361805312': '韩语',
'6997855138153095168': '日语',
'7005423896983887872': '音悦人',
}

#cache = {
res: {},
items: [],
#opts = {
uptime: 0,
expire: 24*60,
channel: '6998499728862334976',
channels: {
'6998499728862334976': '华语',
'6951459197061984256': '欧美',
'6998475633361805312': '韩语',
'6997855138153095168': '日语',
'7005423896983887872': '音悦人',
},
}
#cache = new Map();

async createFilter(filter) {
return {
"data": {
title: "",
title: "Channel",
max: 1,
min: 1,
default: this.#channelDefault,
options: this.#channelList,
default: this.#opts.channel,
options: this.#opts.channels,
}
}
}

async reqJSON(channelid, page) {
async latest(page) {
return await this.getBangumis(this.#opts.channel, page);
}

async search(kw, page, filter) {
const channelid = filter?.data && filter.data[0] || '';
if(channelid && !kw) {
this.#opts.channel = channelid;
return await this.getBangumis(channelid, page);
}
const res = await this.getCacheAll();
if(kw) {
kw = kw.toLowerCase();
return res.filter((v) => ~v.title.toLowerCase().indexOf(kw));
}
return res;
}

async detail(url) {
const res = await this.getCacheAll();
const bangumi = res.find((v) => v.url === url);
bangumi.episodes = [{
title: 'Clip',
urls: bangumi.urls.map(v => {
return {
name: v.display,
url: v.url
}
})
}];
return bangumi
}

async watch(url) {
return {
type: 'hls',
url
}
}

async getCacheAll() {
if (this.#cache.size < 1) {
return [];
}
const bangumi = [];
const values = this.#cache.values();
let v;
while(v = values.next().value) {
bangumi.push(v);
}
return bangumi.flat();
}

async getBangumis(channelid, page) {
const size = 20;
const offsets = page*size;
const baseUrl = `/video/explore/channelVideos?channelId=${channelid}&detailType=2&size=${size}&offset=${offsets}`;
const res = await this.request(baseUrl, {
cache: 'no-cache',
headers: {
'Content-Type': 'application/json',
}
});
if ('data' in res) {
return res.data;
const md5path = md5(baseUrl);
if (this.checkCache(md5path)) {
return this.#cache.get(md5path);
}
return [];
}

async getResource(channelid, page) {
const res = await this.reqJSON(channelid, page);
const res = await this.reqJSON(baseUrl);
const bangumi = [];
~res.length && res.forEach(v => {
const title = `${v.allArtistNames} - ${v.title}`;
Expand All @@ -72,49 +116,28 @@ export default class extends Extension {
//artists: v.allArtistNames,
})
})
this.#cache.items = this.#cache.items.concat(bangumi);
this.#cache.set(md5path, bangumi);
this.#opts.uptime = Date.now();
return bangumi;
}

async latest(page) {
return await this.getResource(this.#channelDefault, page);
}

async search(kw, page, filter) {
const channelid = filter?.data && filter.data[0] || "";
if(channelid && !kw) {
this.#channelDefault = channelid;
const res = await this.getResource(channelid, page);
return res;
}
if(kw) {
kw = kw.toLowerCase();
const res = this.#cache.items.filter((v) => ~v.title.toLowerCase().indexOf(kw));
return res;
}
return this.#cache.items;
}

async detail(url) {
const bangumi = this.#cache.items.find((v) => v.url === url);
bangumi.episodes = [
{
title: 'Clip',
urls: bangumi.urls.map(v => {
return {
name: v.display,
url: v.url
}
})
async reqJSON(path) {
const res = await this.request(path, {
cache: 'no-cache',
headers: {
'Content-Type': 'application/json',
}
];
return bangumi
});
if ('data' in res) {
return res.data;
}
return [];
}

async watch(url) {
return {
type: 'hls',
url
}
checkCache(item) {
const expire = +(this.#opts.expire);
return this.#cache.has(item) &&
expire > 0 &&
(Date.now() - this.#opts.uptime) < expire * 60 * 1000;
}
}
88 changes: 41 additions & 47 deletions repo/client.iptv.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,29 @@
// ==MiruExtension==
// @name MyIPTV
// @description A simple IPTV client
// @version v0.0.5
// @version v0.0.6
// @author vvsolo
// @lang all
// @license MIT
// @package client.iptv
// @type bangumi
// @icon https://avatars.githubusercontent.com/u/55937028?s=200&v=4
// @webSite https://
// @icon https://s11.ax1x.com/2024/01/11/pFCMKit.png
// @webSite https://live.fanmingming.com
// @nsfw false
// ==/MiruExtension==


export default class extends Extension {
//https://raw.githubusercontent.com
//https://fastly.jsdelivr.net/gh
//https://gcore.jsdelivr.net/gh
//https://jsdelivr.b-cdn.net/gh
//https://github.moeyy.xyz/https://raw.githubusercontent.com
#rawurl = 'https://github.moeyy.xyz/https://raw.githubusercontent.com';
#opts = {
url: 'https://live.fanmingming.com/tv/m3u/ipv6.m3u',
exturl: this.#rawurl + `/vvsolo/miru-extension-MyIPTV-sources@main/sources.json`,
exturl: "https://cdn.jsdelivr.net/gh/vvsolo/miru-extension-MyIPTV-sources/sources.json",
lists: {
'none': '',
'🇨🇳 fanmingming-IPV6': 'https://live.fanmingming.com/tv/m3u/ipv6.m3u',
'🇨🇳 fanmingming-IPV4': 'https://live.fanmingming.com/tv/m3u/v6.m3u',
'🇨🇳 MyIPTV-IPV6': this.#rawurl + `/vvsolo/miru-extension-MyIPTV-sources@main/ipv6.m3u`,
'🇨🇳 MyIPTV-IPV4': this.#rawurl + `/vvsolo/miru-extension-MyIPTV-sources@main/ipv4.m3u`,
'🇨🇳 YueChan-IPV6': this.#rawurl + `/YueChan/Live@main/IPTV.m3u`,
'🇨🇳 YueChan-Radio': this.#rawurl + `/YueChan/Live@main/Radio.m3u`,
'🇨🇳 YanG-1989-Gather': this.#rawurl + `/YanG-1989/m3u@main/Gather.m3u`,
'🇨🇳 BESTV': this.#rawurl + `/Ftindy/IPTV-URL@main/bestv.m3u`,
"🇨🇳 蓝鲸": this.#rawurl + `/Cyril0563/lanjing_live@main/TVbox_Free/LIVE/Free/HD_LIVE.txt`,
"🇨🇳 乐青多源": this.#rawurl + `/lqtv/lqtv.github.io/main/m3u/tv.m3u`,
"none": "",
"🇨🇳 fanmingming-IPV6": "https://live.fanmingming.com/tv/m3u/ipv6.m3u",
"🇨🇳 MyIPTV-IPV6": "https://cdn.jsdelivr.net/gh/vvsolo/miru-extension-MyIPTV-sources/ipv6.m3u",
"🇨🇳 MyIPTV-IPV4": "https://cdn.jsdelivr.net/gh/vvsolo/miru-extension-MyIPTV-sources/ipv4.m3u",
"🇨🇳 MyIPTV-VOD": "https://cdn.jsdelivr.net/gh/vvsolo/miru-extension-MyIPTV-sources/ipv4.vod.m3u",
"🇨🇳 MyIPTV-RADIO": "https://cdn.jsdelivr.net/gh/vvsolo/miru-extension-MyIPTV-sources/radio.m3u",
}
}
#group = {
Expand All @@ -50,38 +40,30 @@ export default class extends Extension {
exturl: null
}

fixUrl(path) {
return ~path.search(/raw\.githubusercontent\.com/) ? path.replace(/@(main|master)\//, '\/$1\/') : path;
}

async cacheJSON() {
if (this.#cache.exturl && await this.checkExpire()) {
return this.#cache.exturl;
}
const res = await this.request('', {
headers: {
'Content-Type': 'application/json',
'Miru-Url': this.fixUrl(this.#opts.exturl)
'Miru-Url': this.#opts.exturl
}
});
return (this.#cache.exturl = res);
}

async load() {
const opts = this.#opts.lists;
for(let item in opts) {
opts[item] = this.fixUrl(opts[item]);
}
const res = await this.cacheJSON();
Object.assign(opts, res || {});
const lists = this.#opts.lists;
Object.assign(lists, (await this.cacheJSON()) || {});

await this.registerSetting({
title: 'Built-in Source',
key: 'builtin',
type: 'radio',
description: 'Choose the `Custom Source` below when you choose "None"',
defaultValue: '',
options: opts
options: lists
});
await this.registerSetting({
title: 'Custom Source',
Expand Down Expand Up @@ -161,24 +143,31 @@ export default class extends Extension {
const res = (await this.req(baseUrl))
.replace(/\r?\n/g, '\n')
.replace(/\n+/g, '\n')
// fix
.replace(/\.m3u8\?\n([\w\=\-&]+)\n/g, '.m3u8?$1\n')
.replace(/\.m3u8\n\?([\w\=\-&]+)\n/g, '.m3u8?$1\n')
.replace(/^(#EXTINF:\-?[\d\.]+) *\,/gmi, '$1 ')
.replace(/^(#EXTINF:\-?[\d\.]+) ([^,]+)$/gmi, '$1,$2')
.trim();

const ext = baseUrl.slice(baseUrl.lastIndexOf('.')).toLowerCase();
const content = res.split('\n');
let bangumi = [];

const repeats = {};
if (~res.search(/#genre#/i) || ext === '.txt') {
let group, tmp;
const repeats = {};
content.forEach((item) => {
tmp = item.split(',');
if (tmp.length !== 2) {
// 有 genre 标记取 group 名称
if (~item.search(/,#genre#$/i)) {
group = item.split(',#')[0];
return;
}
const [title, url] = tmp;
if (url === '#genre#') {
group = title;
// 无分隔 标记取 group 名称
if (!~item.search(/,/) && !~item.search(/(?:https?|rs[tcm]p|rsp|mms|udp)/)) {
group = item;
return;
}
let [title, url] = item.split(',');
// 组合相同名称的台
if (title in repeats) {
repeats[title].url += '#' + url;
Expand All @@ -191,9 +180,7 @@ export default class extends Extension {
group
}
});
// 组合相同名称的台
bangumi = Object.values(repeats);
} else if (~res.search(/#EXT(?:M3U|INF)/i) || ext === '.m3u') {
} else if (~res.search(/#EXT(?:M3U|INF)/i) || ['.m3u', '.m3u8'].includes(ext)) {
let title, cover, group;
let headers = {};
const vlcopt = {
Expand All @@ -209,19 +196,26 @@ export default class extends Extension {
for (let v in vlcopt) if (item.startsWith(vlcopt[v])) {
headers[v] = item.slice(vlcopt[v].length);
}
} else if (title && ~item.search(/^(?:https?|rs[tcm]p|rsp|mms)/) && !~item.search(/\.mpd/)) {
bangumi.push({
} else if (title && ~item.search(/^(?:https?|rs[tcm]p|rsp|mms|udp)/) && !~item.search(/\.mpd/)) {
// 组合相同名称的台
if (title in repeats && repeats[title].group === group) {
repeats[title].url += '#' + item.trim();
return;
}
repeats[title] = {
title,
url: item.trim(),
cover,
group,
headers,
});
}
title = '';
headers = {};
}
});
}
// 组合相同名称的台
const bangumi = Object.values(repeats) || [];
this.#cache.uptime = Date.now();
return (this.#cache.items = this.#cache.res[md5path] = bangumi);
}
Expand All @@ -230,7 +224,7 @@ export default class extends Extension {
if (page > 1) {
return [];
}
!~this.#cache.items.length && await this.latest();
!~this.#cache.items.length && (await this.latest());
const filt = filter?.data && filter.data[0] || this.#group.val;
const bangumi = this.#cache.items;
if (filt === this.#group.val) {
Expand Down
Loading
Loading