Skip to content
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
39 changes: 3 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Liver ![version](https://img.shields.io/badge/Version-v0.0.2-pink?style=for-the-badge&logo)
# Liver ![version](https://img.shields.io/badge/Version-v0.0.3-pink?style=for-the-badge&logo)

<a><img align="right" src="https://i.imgur.com/F9qenDY.png"></a>

Expand All @@ -10,13 +10,12 @@ Liver is designed to be a fast and simple way to check Livers stream status.
Get it for [Chrome](https://chrome.google.com/webstore/detail/liver/pjnhlmepkmjikjjmbaiabncnhcbkphfh?hl=en&authuser=0)

## Demo

https://user-images.githubusercontent.com/19758116/223499125-e0670046-c6e6-4348-8d4e-0cac1b0ca473.mov

## Images

<img src="https://i.imgur.com/FTHCArK.png" width="280"><img src="https://i.imgur.com/wLZK69d.png" width="280">
<img src="https://i.imgur.com/RViLUqa.png" width="280"><img src="https://i.imgur.com/rZykb4w.png" width="280">
<img src="https://i.imgur.com/YHybind.png" width="560">

<br />

Expand All @@ -26,35 +25,3 @@ https://user-images.githubusercontent.com/19758116/223499125-e0670046-c6e6-4348-
-TypeScript \
-TailwindCSS \
-Chrome Storage API

<br />

## Use extension with your own API key

Make sure you have [Node.js](https://nodejs.org/) installed. \
Run these commands to get the project locally:

```sh
git clone https://github.com/zigamacele/liver.git
cd liver
npm install
```

Download extension from Releases and extract it outside of repo you just cloned.
<img src="https://i.imgur.com/itX1DOp.png" width="280">

Get your own API key from [Holodex](https://docs.holodex.net/docs/holodex/ZG9jOjQ2Nzk1-getting-started)

Go back into the repo you just cloned, create .env file and paste in your API key.

```sh
NEXT_PUBLIC_HOLODEX=YOUR_API_KEY
```

Run 'npm run build' in your terminal from the repo directory.

```sh
npm run build
```

[Now add the extension to your Chrome browser.](https://www.youtube.com/watch?v=oswjtLwCUqg)
29 changes: 27 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "liver-app",
"version": "0.0.2",
"version": "0.0.3",
"private": true,
"scripts": {
"dev": "next dev",
Expand All @@ -9,6 +9,7 @@
"lint": "next lint"
},
"dependencies": {
"@headlessui/react": "^1.7.13",
"@heroicons/react": "^2.0.16",
"@next/font": "13.1.6",
"@types/chrome": "^0.0.217",
Expand All @@ -18,6 +19,7 @@
"axios": "^1.3.3",
"eslint": "8.34.0",
"eslint-config-next": "13.1.6",
"moment": "^2.29.4",
"next": "13.1.6",
"react": "18.2.0",
"react-chrome-extension-router": "^1.4.0",
Expand Down
131 changes: 68 additions & 63 deletions src/Components/DisplayMyLivers.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { database } from '@/database';
import moment from 'moment';
import React, { useEffect, useState } from 'react';
import 'react-pulse-dot/dist/index.css';
import ClipLoader from 'react-spinners/ClipLoader';

// @ts-ignore
import PulseDot from 'react-pulse-dot';

import { AiFillTwitterCircle } from 'react-icons/ai';

export default function DisplayMyLivers() {
import { DisplayedLiver } from './DisplayedLiver';

export default function DisplayMyLivers({
showStreamTitle,
setShowStreamTitle,
}: {
showStreamTitle: string;
setShowStreamTitle: Function;
}) {
const [displayLivers, setDisplayLivers] = useState([]);
const [liverStatus, setLiverStatus] = useState({});
const [databaseInfo, setDatabaseInfo] = useState<vtubersFromDB>({});
Expand All @@ -28,6 +30,20 @@ export default function DisplayMyLivers() {
[key: string]: vtuberInfo;
}

interface Members {
name: string;
imageURL: string;
channelID: string;
retired: boolean;
twitter: string;
}

interface Branch {
branchID: string;
debut: string;
members: Members[];
}

useEffect(() => {
getMyLivers();
getAPIData();
Expand All @@ -53,11 +69,13 @@ export default function DisplayMyLivers() {
async function databaseSearch() {
let tempDatabase = {};
displayLivers.forEach((memberID) => {
database.forEach((branch) => {
branch.members.forEach((member) => {
if (member.channelID === memberID) {
tempDatabase = { ...tempDatabase, [memberID]: member };
}
Object.keys(database).forEach((group) => {
database[group].forEach((branch: Branch) => {
branch.members.forEach((member: Members) => {
if (member.channelID === memberID) {
tempDatabase = { ...tempDatabase, [memberID]: member };
}
});
});
});
});
Expand Down Expand Up @@ -88,10 +106,20 @@ export default function DisplayMyLivers() {
memberID === APIResponse[index].channel.id &&
APIResponse[index].status === 'live'
)
tempLiveStatus = { ...tempLiveStatus, [memberID]: 'live' };
tempLiveStatus = {
...tempLiveStatus,
[memberID]: {
status: 'live',
title: APIResponse[index].title,
started: APIResponse[index].start_actual,
},
};
}
if (!Object.keys(tempLiveStatus).includes(memberID))
tempLiveStatus = { ...tempLiveStatus, [memberID]: 'offline' };
tempLiveStatus = {
...tempLiveStatus,
[memberID]: { status: 'offline' },
};

if (Object.keys(tempLiveStatus).length === displayLivers.length) {
setLiverStatus(tempLiveStatus);
Expand All @@ -101,64 +129,41 @@ export default function DisplayMyLivers() {
}

function handleTwitter(memberID: string) {
database.forEach((branch) => {
branch.members.forEach((member) => {
const url = 'https://twitter.com/';
if (member.channelID === memberID) {
chrome.tabs.create({ url: url + member.twitter });
}
Object.keys(database).forEach((group) => {
database[group].forEach((branch: Branch) => {
branch.members.forEach((member: Members) => {
if (member.channelID === memberID) {
const url = 'https://twitter.com/';
chrome.tabs.create({ url: url + member.twitter });
}
});
});
});
}

function startedStreaming(startTime: string) {
const difference = +new Date() - +new Date(startTime);
const diffDuration = moment.duration(-difference).humanize(true);
if (diffDuration === 'Invalid date') return 'starting soon';
return diffDuration;
}

return (
<div className="my-2">
<div className="flex flex-wrap justify-center gap-3">
{displayLivers.map((memberID) => {
const url = `https://youtube.com/channel/${memberID}/live`;
return (
<div key={memberID}>
{!databaseInfo[memberID] ? null : (
<div className="relative">
<img
src={databaseInfo[memberID].imageURL}
alt={databaseInfo[memberID].name}
className="rounded-full h-20 liver border-4 border-white dark:border-slate-700 bg-slate-200 dark:bg-slate-800 cursor-pointer hover:opacity-80"
onClick={(e) => {
chrome.tabs.create({ url: url });
}}
/>
<div
className="text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 cursor-pointer absolute left-[4em] bottom-[4em]"
onClick={() => handleTwitter(memberID)}
>
<AiFillTwitterCircle className="absolute text-lg left-[1.15em] bottom-[-0.85em] z-10 " />
<span className="absolute text-xl left-[0.975em] bottom-[-0.82em] bg-white dark:bg-slate-700 h-5 w-5 rounded-full"></span>
</div>

{loading ? (
<div className="absolute left-[4.5em] bottom-[4.5em] bg-white p-1 pb-0 rounded-full dark:bg-slate-700">
<ClipLoader size={15} />
</div>
) : (
<div className="absolute left-[4em] bottom-[4em]">
{liverStatus[memberID] === 'offline' ? (
<div className="absolute left-[-1em] bottom-[0.7em] py-0.5 px-1.5 bg-white dark:bg-slate-700 dark:text-white rounded-full">
<p className="text-[10px]">OFFLINE</p>
</div>
) : (
<div>
<PulseDot
className="absolute text-xl bottom-[-0.15em] z-10"
color="danger"
/>
<span className="absolute text-xl left-[0.3em] bottom-[0.17em] bg-white dark:bg-slate-700 h-7 w-7 rounded-full"></span>
</div>
)}
</div>
)}
</div>
)}
<DisplayedLiver
memberID={memberID}
databaseInfo={databaseInfo}
liverStatus={liverStatus}
handleTwitter={handleTwitter}
startedStreaming={startedStreaming}
loading={loading}
showStreamTitle={showStreamTitle}
setShowStreamTitle={setShowStreamTitle}
/>
</div>
);
})}
Expand Down
Loading