Skip to content

Commit 74c7d0c

Browse files
committed
First queue
1 parent 4e33eac commit 74c7d0c

6 files changed

Lines changed: 255 additions & 11 deletions

File tree

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
"test": "echo \"Error: no test specified\" && exit 1",
88
"prebuild": "rollup -c rollup.config.js && npm run build:other",
99
"start": "concurrently \"npm:watch:ts\" \"npm:watch:other\"",
10-
"start:firefox": "npm run prebuild && concurrently \"npm:watch:ts\" \"npm:watch:other\" \"npm:ext:firefox\"",
11-
"start:chromium": "npm run prebuild && concurrently \"npm:watch:ts\" \"npm:watch:other\" \"npm:ext:chromium\"",
10+
"start:firefox": "[ -d extension-dist ] || npm run prebuild; concurrently \"npm:watch:ts\" \"npm:watch:other\" \"npm:ext:firefox\"",
11+
"start:chromium": "[ -d extension-dist ] || npm run prebuild; concurrently \"npm:watch:ts\" \"npm:watch:other\" \"npm:ext:chromium\"",
1212
"build": "npm run clean && npm run build:ts && npm run build:other && npm run ext",
1313
"ext:firefox": "web-ext run --source-dir ./extension-dist/ --keep-profile-changes -p dev",
1414
"ext:android": "web-ext run --source-dir ./extension-dist/ --target firefox-android --keep-profile-changes -p dev",

src/icons/play.svg

Lines changed: 5 additions & 0 deletions
Loading

src/manifest.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
"scripts/pages/zype/zype.js"
3232
],
3333
"permissions": [
34-
"storage"
34+
"storage",
35+
"*://api.zype.com/*"
3536
],
3637
"browser_action": {
3738
"default_icon": {

src/scripts/pages/watchnebula/_nebula.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
const videoselector = 'a[href^="/videos/"]';
22
import svg from "./../../../icons/watchlater.svg";
3+
import { addToStore, enqueue, enqueueNow, gotoNextInQueue, init } from "./_queue";
34

45
export const nebula = () => {
5-
console.log('nebula', document.body);
6-
76
document.body.addEventListener('mouseover', hover);
87
document.body.addEventListener('click', click);
8+
init();
99
};
1010

1111
const imgLink = (e: HTMLElement) => {
@@ -35,13 +35,21 @@ const hover = (e: MouseEvent) => {
3535
const click = (e: MouseEvent) => {
3636
const target = e.target as HTMLElement;
3737
const later = target.closest('.enhancer-queueButton');
38-
if (later === null)
39-
return;
40-
const link = target.closest(videoselector);
38+
const link = target.closest(videoselector) as HTMLAnchorElement;
4139
if (link === null)
4240
return;
43-
const img = later.parentElement.firstElementChild;
44-
// queue button clicked
41+
const img = link.querySelector('img') as HTMLImageElement;
42+
// always prevent going to video
4543
e.preventDefault();
46-
console.log(link, later);
44+
const name = link.getAttribute('href').substr(8);
45+
// extract and store information on video
46+
addToStore(name, img.nextElementSibling?.lastChild.textContent, img.src, link.lastElementChild?.children[1]?.textContent, link.lastElementChild?.lastElementChild?.firstElementChild?.textContent);
47+
if (later !== null) {
48+
// queue button clicked
49+
enqueue(name);
50+
} else {
51+
// video clicked
52+
enqueueNow(name);
53+
gotoNextInQueue();
54+
}
4755
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import play from "../../../icons/play.svg";
2+
3+
export type video = {
4+
length: string,
5+
thumbnail: string,
6+
title: string,
7+
creator: string,
8+
};
9+
10+
const store: { [key: string]: video } = {};
11+
const queue: string[] = [];
12+
let queuepos = 0;
13+
let queueel: HTMLElement = null;
14+
15+
export async function addToStore(name: string, length: string, thumbnail: string, title: string, creator: string): Promise<void>;
16+
export async function addToStore(name: string): Promise<void>;
17+
export async function addToStore(name: string, ...args: any[]) {
18+
if (store[name])
19+
return; // already in
20+
const [ length, thumbnail, title, creator ] = args.length === 4 ? args : await requestData(name);
21+
store[name] = { length, thumbnail, title, creator };
22+
}
23+
export const enqueue = (name: string, pos?: number) => {
24+
if (!store[name])
25+
throw "Not in store!";
26+
if (pos !== undefined)
27+
queue.splice(pos, 0, name);
28+
else
29+
queue.splice(queue.length, 0, name);
30+
console.log(queue);
31+
};
32+
export const enqueueNow = (name: string) => {
33+
if (!name)
34+
throw "Not in store!";
35+
queue.splice(queuepos + 1, 0, name);
36+
console.log(queue);
37+
}
38+
export const removeFromQueue = (index: number) => {
39+
queue.splice(index, 1);
40+
console.log(queue);
41+
}
42+
export const gotoQueue = (index: number) => {
43+
queueel?.children[queuepos]?.classList.remove('playing');
44+
queuepos = index;
45+
const url = `/videos/${queue[index]}`;
46+
// trick router into accepting my url without reloading page
47+
window.history.pushState(null, null, url);
48+
window.history.pushState(null, null, url);
49+
window.history.back();
50+
queueel?.children[index]?.classList.add('playing');
51+
console.log(queuepos);
52+
}
53+
export const gotoNextInQueue = () => gotoQueue(queuepos + 1);
54+
55+
56+
export const init = () => {
57+
// remove existing elements from extension reload
58+
Array.from(document.querySelectorAll('.enhancer-queue')).forEach(e => e.remove());
59+
60+
const q = document.createElement('div');
61+
q.className = 'enhancer-queue';
62+
q.innerHTML = `
63+
<div class="top"></div>
64+
<div class="elements"></div>
65+
`;
66+
const e = q.querySelector('.elements') as HTMLElement;
67+
queueel = e;
68+
document.body.appendChild(q);
69+
70+
queue.splice = (start: number, count: number, ...elements: string[]) => {
71+
start = start < 0 ? queue.length - start : start;
72+
start = start < 0 ? 0 : start > queue.length ? queue.length : start;
73+
const end = start + count > queue.length ? queue.length : start + count;
74+
let s = start > 0 ? e.children[start-1] : null;
75+
for (let i = start; i < end; i++)
76+
e.children[i].remove();
77+
for (let el of elements) {
78+
const node = insertChild(el);
79+
s = s === null ? e.appendChild(node) : (s.after(node), node);
80+
}
81+
return Array.prototype.splice.call(queue, start, count, ...elements);
82+
};
83+
e.addEventListener('click', clickElements);
84+
window.addEventListener('message', msg);
85+
};
86+
const clickElements = (e: MouseEvent) => {
87+
const el = (e.target as HTMLElement).closest('.element');
88+
if (el === null)
89+
return;
90+
e.preventDefault();
91+
const i = Array.from(el.parentElement.children).findIndex(e => e.isSameNode(el));
92+
gotoQueue(i);
93+
};
94+
const msg = (e: MessageEvent) => {
95+
if (e.origin !== "https://player.zype.com" && e.origin !== "http://player.zype.com")
96+
return;
97+
const m = JSON.parse(e.data);
98+
if (m.event === "zype:complete") gotoNextInQueue();
99+
}
100+
101+
102+
type thumb = {
103+
aspect_ratio: number,
104+
height: number,
105+
width: number,
106+
url: string,
107+
name: string,
108+
};
109+
type cat = {
110+
_id: string,
111+
category_id: string,
112+
title: string,
113+
value: string[],
114+
};
115+
const requestData = async (name: string) => {
116+
const data = await fetch(`https://api.zype.com/videos?friendly_title=${name}&per_page=1&api_key=JlSv9XTImxelHi-eAHUVDy_NUM3uAtEogEpEdFoWHEOl9SKf5gl9pCHB1AYbY3QF`, {
117+
"credentials": "omit",
118+
"headers": {
119+
"Accept": "application/json, text/plain, */*",
120+
"Accept-Language": "en-US,en;q=0.5",
121+
"Cache-Control": "max-age=0"
122+
},
123+
"referrer": `https://watchnebula.com/videos/${name}`,
124+
"method": "GET",
125+
"mode": "cors"
126+
}).then(res => res.json()).catch(console.error);
127+
const vid = data.response[0];
128+
return [
129+
`${Math.floor(vid.duration / 60)}:${vid.duration - Math.floor(vid.duration / 60) * 60}`,
130+
(vid.thumbnails as thumb[])[0].url,
131+
vid.title,
132+
(vid.categories as cat[]).find(c => c.value.length).value[0]
133+
] as string[];
134+
};
135+
const insertChild = (name: string): HTMLElement => {
136+
const n = document.createElement('div');
137+
n.className = 'element';
138+
n.innerHTML = `
139+
<div class="thumb">
140+
<img src="${store[name].thumbnail}" />
141+
<div class="play">${play}</div>
142+
</div>
143+
<div class="data">
144+
<span class="title">${store[name].title}</span>
145+
<span class="creator">${store[name].creator}${store[name].length}</span>
146+
</div>
147+
`;
148+
return n;
149+
};

src/styles/content.css

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,85 @@
2121
.enhancer-queueButton:hover svg {
2222
width: 22px;
2323
height: 22px;
24+
}
25+
26+
27+
.enhancer-queue {
28+
position: fixed;
29+
bottom: 0;
30+
right: 0;
31+
width: 360px;
32+
background-image: linear-gradient(0deg, var(--bg-color), var(--bg-color));
33+
background-color: #111;
34+
background-blend-mode: multiply;
35+
border-top-left-radius: 5px;
36+
overflow: hidden;
37+
}
38+
.enhancer-queue .element {
39+
display: flex;
40+
flex-direction: row;
41+
flex-wrap: nowrap;
42+
}
43+
.enhancer-queue .element:hover {
44+
cursor: pointer;
45+
}
46+
.enhancer-queue .element img {
47+
height: 90px;
48+
width: auto;
49+
}
50+
.enhancer-queue .element .data {
51+
display: flex;
52+
flex-direction: column;
53+
align-self: center;
54+
margin: auto;
55+
padding: 12px 8px;
56+
overflow: hidden;
57+
}
58+
.enhancer-queue .element .data .title {
59+
font-size: 16px;
60+
max-height: 48px;
61+
display: -webkit-box;
62+
-webkit-line-clamp: 2;
63+
-webkit-box-orient: vertical;
64+
}
65+
.enhancer-queue .element .data .creator {
66+
font-size: 11px;
67+
text-overflow: ellipsis;
68+
white-space: nowrap;
69+
}
70+
.enhancer-queue .element .data .title,
71+
.enhancer-queue .element .data .creator {
72+
overflow: hidden;
73+
text-align: left;
74+
width: 100%;
75+
}
76+
.enhancer-queue .element .thumb {
77+
position: relative;
78+
flex-shrink: 0;
79+
}
80+
.enhancer-queue .element .thumb .play {
81+
display: none;
82+
position: absolute;
83+
top: 0;
84+
left: 0;
85+
width: 100%;
86+
height: 100%;
87+
}
88+
.enhancer-queue .element .thumb .play svg {
89+
width: 100%;
90+
height: 100%;
91+
padding: 10px;
92+
}
93+
.enhancer-queue .element.playing .thumb .play {
94+
display: block;
95+
}
96+
.enhancer-queue .element.playing .thumb::after {
97+
content: '';
98+
position: absolute;
99+
top: 0;
100+
left: 0;
101+
width: 100%;
102+
height: 100%;
103+
background-color: var(--font-color);
104+
opacity: 0.4;
24105
}

0 commit comments

Comments
 (0)