aboutsummaryrefslogtreecommitdiffhomepage
path: root/ci/pipedCI.ts
blob: 80424295c94f2f28d89cb7fe35764642a9896f7c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import * as data from "../ci/piped_instances.json";

type percent = string
type dailyMinutesDown = Record<string, number>

type PipedInstance = {
  name: string;
  url: string;
  icon: string;
  slug: string;
  status: string;
  uptime: percent;
  uptimeDay: percent;
  uptimeWeek: percent;
  uptimeMonth: percent;
  uptimeYear: percent;
  time: number;
  timeDay: number;
  timeWeek: number;
  timeMonth: number;
  timeYear: number;
  dailyMinutesDown: dailyMinutesDown
}

const percentNumber = (percent: percent) => Number(percent.replace("%", ""))
const ninetyDaysAgo = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000)

function dailyMinuteFilter (dailyMinutesDown: dailyMinutesDown) {
  let daysDown = 0
  for (const [date, minsDown] of Object.entries(dailyMinutesDown)) {
    if (new Date(date) >= ninetyDaysAgo && minsDown > 1000) { // if within 90 days and down for more than 1000 minutes
      daysDown++
    }
  }
  // return true f less than 10 days down
  return daysDown < 10
}

const getHost = (url: string) => new URL(url).host

const getWatchPage = async (instance: PipedInstance) =>
  fetch(`https://${getHost(instance.url)}`, { redirect: "manual" })
    .then(res => res.headers.get("Location"))
    .catch(e => { console.log (e); return null })

const siteOK = async (instance) => {
  // check if entire site is redirect
  const notRedirect = await fetch(instance.url, { redirect: "manual" })
    .then(res => res.status == 200)
  // only allow kavin to return piped.video
  // if (instance.url.startsWith("https://piped.video") && instance.slug !== "kavin-rocks-official") return false
  // check if frontend is OK
  const watchPageStatus = await fetch(instance.frontendUrl)
    .then(res => res.ok)
  // test API - stream returns ok result
  const streamStatus = await fetch(`${instance.apiUrl}/streams/BaW_jenozKc`)
    .then(res => res.ok)
  // get startTime of monitor
  const age = await fetch(instance.historyUrl)
    .then(res => res.text())
    .then(text => { // startTime greater than 90 days ago
      const date = text.match(/startTime: (.+)/)[1]
      return Date.parse(date) < ninetyDaysAgo.valueOf()
    })
  // console.log(notRedirect, watchPageStatus, streamStatus, age, instance.frontendUrl, instance.apiUrl)
  return notRedirect && watchPageStatus && streamStatus && age
}

const staticFilters = (data as PipedInstance[])
  .filter(instance => {
    const isup = instance.status === "up"
    const monthCheck = percentNumber(instance.uptimeMonth) >= 90
    const dailyMinuteCheck = dailyMinuteFilter(instance.dailyMinutesDown)
    return isup && monthCheck && dailyMinuteCheck
  })
  .map(async instance => {
    // get frontend url
    const frontendUrl = await getWatchPage(instance)
    if (!frontendUrl) return null // return false if frontend doesn't resolve
    // get api base
    const apiUrl = instance.url.replace("/healthcheck", "")
    const historyUrl = `https://raw.githubusercontent.com/TeamPiped/piped-uptime/master/history/${instance.slug}.yml`
    const pass = await siteOK({ apiUrl, historyUrl, frontendUrl, url: instance.url })
    const frontendHost = getHost(frontendUrl)
    return pass ? frontendHost : null
  })

export async function getPipedList(): Promise<string[]> {
  const instances = await Promise.all(staticFilters)
    .then(arr => arr.filter(i => i !== null))
  return instances
}