Merge pull request 'main' (#7) from ashley/poke:main into main

Reviewed-on: https://codeberg.org/Korbs/poketube/pulls/7
This commit is contained in:
Korbs 2024-02-13 19:55:18 +00:00
commit 0d7aa92eaa
39 changed files with 3942 additions and 790 deletions

19
.drone.yml Normal file
View file

@ -0,0 +1,19 @@
kind: pipeline
type: exec
name: Build and Push Docker Image (Quay)
platform:
os: linux
arch: amd64
steps:
- name: Build
environment:
QUAY_USERNAME:
from_secret: QUAY_USERNAME
QUAY_PASSWORD:
from_secret: QUAY_PASSWORD
commands:
- echo $QUAY_PASSWORD | docker login quay.io --username $QUAY_USERNAME --password-stdin
- docker build -t quay.io/sudovanilla/poketube .
- docker push quay.io/sudovanilla/poketube

View file

@ -1,4 +1,4 @@
# Contributor Covenant Code of Conduct # Contributor Covenant Code of Conduct / PokeTube code of conduct
## Our Pledge ## Our Pledge
@ -60,15 +60,15 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at reported to the community leaders responsible for enforcement at
iamashley@duck.com (E-mail) https://discord.gg/pfKSQ3pMfW (Discord server). iamashley@duck.com (E-mail) https://discord.gg/pfKSQ3pMfW (Discord server) https://matrix.to/#/#poke:vern.cc (matrix space) and https://rvlt.gg/poke (revolt server).
All complaints will be reviewed and investigated promptly and fairly. All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the All community leaders are obligated to respect the privacy and security of the
reporter of any incident. reporter of any incident.
## Additional Terms for Poketube ## Additional Terms for Poketube
**TL;DR**: You are encouraged not to edit or remove these terms from Poketube. While you have the freedom to make changes in your Poketube fork, if you choose to modify this document, please refrain from using the title "Poketube Code of Conduct." Everyone can copy and share this document as is, but making changes is allowed with the aforementioned condition. If your chosen alternative code of conduct doesn't include provisions against hate speech, inappropriate behavior, anti-immigrant sentiments, far-right, or authoritarian content, it's not recommended.
**TL;DR**: You are not allowed to edit or remove these terms from Poketube. You can't remove this file from your Poketube fork. Everyone can copy and share this document as is, but making changes is not allowed. If your chosen alternative code of conduct doesn't include provisions against hate speech, inappropriate behavior, anti-immigrant sentiments, far-right or authoritarian content, it's not allowed.
1. Definitions 1. Definitions
@ -80,7 +80,7 @@ reporter of any incident.
- **Inappropriate Behavior**: Inappropriate behavior encompasses actions or expressions that create an unwelcome, hostile, or offensive environment for others, such as harassment, intimidation, or bullying. - **Inappropriate Behavior**: Inappropriate behavior encompasses actions or expressions that create an unwelcome, hostile, or offensive environment for others, such as harassment, intimidation, or bullying.
- **Authoritarianism**: Authoritarianism is characterized by an emphasis on strong central authority, limited individual freedoms, and restrictions on democratic processes. Content or behavior that promotes authoritarian principles, suppresses freedom of speech, individual rights, or democratic values is strictly prohibited. - **Authoritarianism**: Authoritarianism is characterized by an emphasis on strong central authority, limited individual freedoms, and restrictions on democratic processes. Content or behavior that promotes authoritarian principles, suppresses freedom of speech, individual rights, or democratic values is strongly discouraged.
- **Protected characteristics** include attributes such as race, ethnicity, gender, religion, sexual orientation, disability, and other traits or qualities safeguarded from discrimination by relevant laws and regulations. This defines what is meant by "protected characteristics" in the context of this document. - **Protected characteristics** include attributes such as race, ethnicity, gender, religion, sexual orientation, disability, and other traits or qualities safeguarded from discrimination by relevant laws and regulations. This defines what is meant by "protected characteristics" in the context of this document.
@ -88,27 +88,24 @@ reporter of any incident.
NOTE: The Contributor Covenant Code of Conduct already includes provisions on some of these issues. Our intention is to provide a more defined and explicit statement regarding these prohibitions to ensure a clear and inclusive community environment. NOTE: The Contributor Covenant Code of Conduct already includes provisions on some of these issues. Our intention is to provide a more defined and explicit statement regarding these prohibitions to ensure a clear and inclusive community environment.
YOU ARE ABSOLUTELY AND UNEQUIVOCALLY PROHIBITED FROM EDITING, REMOVING, OR ALTERING THE TERMS OF THIS FILE IN ANY WAY, SHAPE, OR FORM. FURTHERMORE, ONLY ASHLEY (THE AUTHOR) IS PERMITTED TO EDIT THIS CODE OF CONDUCT. YOU MAY NOT, UNDER ANY CIRCUMSTANCES, REMOVE THIS FILE FROM YOUR FORK OF POKETUBE. EVERY INDIVIDUAL, WITHOUT EXCEPTION, IS PERMITTED TO CREATE UNMODIFIED COPIES OF THIS DOCUMENT AND DISTRIBUTE IT AS IS; HOWEVER, THE MODIFICATION OF THIS DOCUMENT IS STRICTLY, UNMISTAKABLY, AND CATEGORICALLY FORBIDDEN. (The contact URLs within this code of conduct may be updated as needed to ensure accurate communication channels.) YOU ARE NOT ENCOURAGED TO EDIT, REMOVE, OR ALTER THE TERMS OF THIS FILE. However, should you choose to make changes, please avoid using the title "Poketube Code of Conduct." Removing this file from your Poketube fork is allowed. Everyone, without exception, is permitted to create unmodified copies of this document and distribute it as is; however, modifications to this document are allowed with the aforementioned condition.
If you do not wish to adhere to this conduct in your fork of Poketube, you have the option to utilize alternative codes of conduct instead. However, if you do not wish to adopt the alternative code of conduct, please feel free to contact us and explain your reasons for wanting to remove this file. It is of paramount importance to emphasize that the promotion or glorification of anti-immigrant sentiments, the alignment with far-right ideologies, Islamophobia, or any form of religious discrimination is strongly discouraged within the scope of Poketube. We maintain a stance against such content, which includes material that discriminates against immigrants, promotes hatred or hostility towards religious groups, or actively supports extremist beliefs associated with far-right ideologies. This stance is encouraged and non-binding.
It is of paramount importance to emphasize that the promotion or glorification of anti-immigrant sentiments, the alignment with far-right ideologies, Islamophobia, or any form of religious discrimination is entirely and unequivocally prohibited within the scope of Poketube. We maintain a zero-tolerance stance on such content, which includes any material that discriminates against immigrants, promotes hatred or hostility towards religious groups, or actively supports extremist beliefs associated with far-right ideologies. This stance is unwavering and non-negotiable. We believe in fostering an environment that is inclusive, respectful, and free from discrimination or the promotion of extremist ideologies. As such, any content found in violation of this encouragement will be addressed promptly and appropriately.
We believe in fostering an environment that is inclusive, respectful, and free from discrimination or the promotion of extremist ideologies. As such, any content found in violation of this prohibition will be addressed promptly and appropriately. THE CLARITY AND FORCE OF THIS STATEMENT ARE INTENDED TO ENCOURAGE CLEAR GUIDELINES: ANTI-IMMIGRANT SENTIMENTS, FAR-RIGHT IDEOLOGIES, ISLAMOPHOBIA, RELIGIOUS DISCRIMINATION, MISOGYNY, AND SEXISM ARE STRONGLY DISCOURAGED AND NOT PREFERRED WITHIN OUR COMMUNITY. OUR HOPE IS TO MAINTAIN A RESPECTFUL AND INCLUSIVE ATMOSPHERE FOR ALL, REGARDLESS OF THEIR BACKGROUND, BELIEFS, OR IDENTITY.
THE CLARITY AND FORCE OF THIS STATEMENT ARE INTENDED TO LEAVE NO ROOM FOR AMBIGUITY: ANTI-IMMIGRANT SENTIMENTS, FAR-RIGHT IDEOLOGIES, ISLAMOPHOBIA, RELIGIOUS DISCRIMINATION, MISOGYNY, AND SEXISM ARE STRICTLY OFF-LIMITS AND WILL NOT BE TOLERATED WITHIN OUR COMMUNITY. OUR COMMITMENT TO MAINTAINING A RESPECTFUL AND INCLUSIVE ATMOSPHERE EXTENDS TO ALL, REGARDLESS OF THEIR BACKGROUND, BELIEFS, OR IDENTITY.
These terms may be subject to change, and any updates will be communicated to the Poketube community. Changes to these terms will be communicated to users. These terms may be subject to change, and any updates will be communicated to the Poketube community. Changes to these terms will be communicated to users.
**3. Application of GNU Kind Communication Policy** **3. Application of GNU Kind Communication Policy**
These terms also apply the principles outlined in the [GNU Kind Communication Policy](https://www.gnu.org/philosophy/kind-communication.html), which encourage respectful and inclusive communication within the Poketube community. These terms also align with the principles outlined in the [GNU Kind Communication Policy](https://www.gnu.org/philosophy/kind-communication.html), which encourage respectful and inclusive communication within the Poketube community.
It is crucial to note that we respect the diverse opinions and beliefs of our users. It is crucial to note that we respect the diverse opinions and beliefs of our users.
***Additional terms end lol*** ***Additional terms end lol***
## Enforcement Guidelines ## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining Community leaders will follow these Community Impact Guidelines in determining

View file

@ -25,7 +25,7 @@ RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg -
RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
RUN apt-get update RUN apt-get update
RUN apt-get -y install nodejs RUN apt-get -y install nodejs npm
# Install Packages # Install Packages
RUN npm install RUN npm install

View file

@ -1,12 +1,28 @@
# see https://codeberg.org/Ashley/poke/issues/59 if you are having problems using poke
<h1 align="center"> <h1 align="center">
<a href="https://poketube.fun/watch?v=9sJUDx7iEJw&quality=medium&=sjohgteojgytrueugtye4jhtytjrjnyıı"> <a href="https://poketube.fun/watch?v=9sJUDx7iEJw&quality=medium&=sjohgteojgytrueugtye4jhtytjrjnyıı">
<img src="https://poketube.fun/css/logo-poke.svg" width="400"> <img src="https://poketube.fun/css/logo-poke.svg" width="400">
<a href="http://www.defectivebydesign.org/drm-free">
<img src="https://static.fsf.org/dbd/label/DRM-free%20label%20120.en.png"
alt="DefectiveByDesign.org"
width="65" height="65" border="0" align="middle" /></a>
</a> </a>
<p>The Ultimate Privacy App</p> <p>The Ultimate Privacy App</p>
</h1> </h1>
<div align="center"> <div align="center">
<span> Be Anonymous watching epic videos, searching thingys on the interwebs and listening to music on poke - the free privacy front end!</span></div> <span> Be Anonymous watching epic videos, searching thingys on the interwebs and listening to music on poke - the free privacy front end!</span>
<span>"Since you work on poke, Are you in touch with its lead developer "Jose marchasi"? <br>
-RMS
Stallman though poke was GNU poke lmaoooo
</span>
</div>
<div align="center"> <div align="center">
[Welcome!](#welcome)&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[Features](#features)&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[No non-free codec needed](#no-non-free-codec-needed-3)&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[Hosting Poke~](#hosting-poketube)&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[Poke community!](#poketube-community)&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[The Legal Stuff (boring tbh)](#the-legal-stuff-boring-tbh) [Welcome!](#welcome)&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[Features](#features)&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[No non-free codec needed](#no-non-free-codec-needed-3)&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[Hosting Poke~](#hosting-poketube)&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[Poke community!](#poketube-community)&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[The Legal Stuff (boring tbh)](#the-legal-stuff-boring-tbh)
@ -27,7 +43,7 @@
## Welcome! ## Welcome!
This is the source code of PokeTube, the privacy-friendly youtube front-end built with the InnerTube API! This is the source code of Poke (formerly PokeTube), the privacy-friendly youtube front-end built with the InnerTube API!
<h1>Features</h1> <h1>Features</h1>
@ -129,11 +145,16 @@ The port can be changed with the config file you downloaded, just change the `se
see [here](https://codeberg.org/Ashley/poke/src/branch/main/january) :3 see [here](https://codeberg.org/Ashley/poke/src/branch/main/january) :3
just uhh change the url in config.json to ur image proxy just uhh change the url in config.json to ur image proxy
## PokeTube community! ## Poke community!
Join the community on [revolt](https://rvlt.gg/poketube) :3 Join the community on [revolt](https://rvlt.gg/poketube) or [matrix](https://matrix.to/#/#poke:vern.cc) :3
## The Legal Stuff (boring tbh) ## The Legal Stuff (boring tbh)
the main parts of the project is Under GPL-3.0-OR-LATER :3
see the each sections LICENSE tho!!
Copyleft 2021-202x Poke Project
[Code Of conduct](https://codeberg.org/Ashley/poketube/src/branch/main/CODE_OF_CONDUCT.md) [Code Of conduct](https://codeberg.org/Ashley/poketube/src/branch/main/CODE_OF_CONDUCT.md)

View file

@ -3,6 +3,7 @@
"invapi": "https://invid-api.poketube.fun/api/v1", "invapi": "https://invid-api.poketube.fun/api/v1",
"dislikes": "https://returnyoutubedislikeapi.com/votes?videoId=", "dislikes": "https://returnyoutubedislikeapi.com/votes?videoId=",
"invchannel": "https://invid-api.poketube.fun/api/v1", "invchannel": "https://invid-api.poketube.fun/api/v1",
"p_url":"https://p.poketube.fun",
"media_proxy": "https://image-proxy.poketube.fun", "media_proxy": "https://image-proxy.poketube.fun",
"cacher_max_age": "864000", "cacher_max_age": "864000",
"enablealwayshttps": false, "enablealwayshttps": false,

View file

@ -98,7 +98,7 @@ function fadeInElements() {
window.addEventListener('scroll', fadeInElements); window.addEventListener('scroll', fadeInElements);
document.addEventListener('fullscreenchange', fadeInElements); document.addEventListener('fullscreenchange', fadeInElements);
setInterval(fadeInElements, 500); setInterval(fadeInElements, 100);
function jumpToTime(e) { function jumpToTime(e) {
e.preventDefault(); e.preventDefault();
@ -202,30 +202,100 @@ function fetchUrls(urls) {
}); });
} }
/* function anondocumenttitle(message, times) {
// Fetch channel URLs var hash = CryptoJS.SHA256(message);
const channelUrls = document.querySelectorAll('a[href*="/channel?id="]');
fetchUrls(channelUrls);
// Fetch download URLs return hash.toString(CryptoJS.enc.Hex);
const downloadUrls = document.querySelectorAll('a[href*="/download?v="]'); }
fetchUrls(downloadUrls);
if(navigator.globalPrivacyControl) {
var gpcValue = navigator?.globalPrivacyControl
} else {
var gpcValue = false
}
if (location.hostname === "poketube.fun") {
if (typeof Ashley === "undefined") {
var Ashley = {};
}
Ashley.dntEnabled = function (dnt, ua) {
"use strict";
var dntStatus =
dnt ||
navigator.doNotTrack ||
window.doNotTrack ||
navigator.msDoNotTrack;
var userAgent = ua || navigator.userAgent;
var anomalousWinVersions = [
"Windows NT 6.1",
"Windows NT 6.2",
"Windows NT 6.3",
];
var fxMatch = userAgent.match(/Firefox\/(\d+)/);
var ieRegEx = /MSIE|Trident/i;
var isIE = ieRegEx.test(userAgent);
var platform = userAgent.match(/Windows.+?(?=;)/g);
if (isIE && typeof Array.prototype.indexOf !== "function") {
return false;
} else if (fxMatch && parseInt(fxMatch[1], 10) < 32) {
dntStatus = "Unspecified";
} else if (
isIE &&
platform &&
anomalousWinVersions.indexOf(platform.toString()) !== -1
) {
dntStatus = "Unspecified";
} else {
dntStatus = { 0: "Disabled", 1: "Enabled" }[dntStatus] || "Unspecified";
}
return dntStatus === "Enabled" ? true : false;
};
// only load if DNT is not enabled
if(!gpcValue) {
if (Ashley && !Ashley.dntEnabled()) {
var _paq = (window._paq = window._paq || []);
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push([
"setDocumentTitle",
anondocumenttitle(document.domain, 5) +
"/" +
anondocumenttitle(document.title, 5),
]);
_paq.push(["setDoNotTrack", true]);
_paq.push(["disableCookies"]);
_paq.push(["trackPageView"]);
_paq.push(["enableLinkTracking"]);
(function () {
var u = "//data.poketube.fun/";
_paq.push(["setTrackerUrl", u + "matomo.php"]);
_paq.push(["setSiteId", "2"]);
var d = document,
g = d.createElement("script"),
s = d.getElementsByTagName("script")[0];
g.async = true;
g.src = u + "matomo.js";
s.parentNode.insertBefore(g, s);
})();
}
}
}
// fetch videos urls
const urls = document.querySelectorAll('a[href*="/watch?v="]');
fetchUrls(urls);
*/
var popupMenu = document.getElementById("popupMenu"); var popupMenu = document.getElementById("popupMenu");
var loopOption = document.getElementById("loopOption"); var loopOption = document.getElementById("loopOption");
var speedOption = document.getElementById("speedOption"); var speedOption = document.getElementById("speedOption");
video.addEventListener("contextmenu", function(event) {
video.addEventListener("contextmenu", function(event) {
// Check if the video is in fullscreen mode
if (!document.fullscreenElement && !document.webkitFullscreenElement && !document.mozFullScreenElement && !document.msFullscreenElement) {
event.preventDefault(); event.preventDefault();
popupMenu.style.display = "block"; popupMenu.style.display = "block";
popupMenu.style.left = event.pageX + "px"; popupMenu.style.left = event.pageX + "px";
popupMenu.style.top = event.pageY + "px"; popupMenu.style.top = event.pageY + "px";
}); }
});
// Hide the popup menu when clicking outside of it // Hide the popup menu when clicking outside of it
window.addEventListener("click", function(event) { window.addEventListener("click", function(event) {
@ -234,13 +304,32 @@ fetchUrls(urls);
} }
}); });
var loopedIndicator = document.getElementById("loopedIndicator");
loopedIndicator.style.display = "none"; // Initially hide the indicator
loopOption.addEventListener("click", function() { loopOption.addEventListener("click", function() {
video.loop = !video.loop; var looped = video.loop;
if (video.loop) { video.loop = !looped;
alert("Looped!");
// Update the looped indicator popup
var displaySpecialText = Math.random() < 0.5;
// Update the looped indicator popup
if (displaySpecialText) {
var specialText = looped ? "Unlooped >.<" : "Looped~ :3 >~<";
loopedIndicator.textContent = specialText;
} else { } else {
alert("unlooped!") loopedIndicator.textContent = looped ? "Unlooped!" : "Looped!";
} }
loopedIndicator.style.display = "block";
// Hide the indicator after 2 seconds
setTimeout(function() {
loopedIndicator.style.display = "none";
}, 2000);
}); });
speedOption.addEventListener("click", function() { speedOption.addEventListener("click", function() {
@ -263,4 +352,6 @@ fetchUrls(urls);
return 2; return 2;
} }
} }
const GoogleTranslateEndpoint = "https://translate.google.com/_/TranslateWebserverUi/data/batchexecute?rpcids=MkEWBc&rt=c"
// @license-end // @license-end

View file

@ -1258,15 +1258,6 @@ nav .right img {
border-radius: 50%; border-radius: 50%;
} }
@font-face {
font-family: "PokeTube Flex";
src: url("https://p.poketube.fun/https://cdn.glitch.global/43b6691a-c8db-41d4-921c-8cf6aa0d9108/robotoflex.ttf?v=1668343428681");
font-style: normal;
font-stretch: 1% 800%;
font-weight: 1 1000;
font-display: swap;
}
.video > .thumbnail > .video-length { .video > .thumbnail > .video-length {
font-size: smaller; font-size: smaller;
background-color: #0008; background-color: #0008;

BIN
css/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

BIN
css/poke-chan-outfit-a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

BIN
css/poke-screnshot-a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 KiB

View file

@ -19,6 +19,58 @@
*/ */
/* latin */
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-weight: 500;
font-stretch: 100%;
src: url(https://p.poketube.fun/https://fonts.bunny.net/montserrat/files/montserrat-latin-500-normal.woff2) format('woff2'), url(https://p.poketube.fun/https://fonts.bunny.net/montserrat/files/montserrat-latin-500-normal.woff) format('woff');
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
/* cyrillic */
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-weight: 500;
font-stretch: 100%;
src: url(https://p.poketube.fun/https://fonts.bunny.net/montserrat/files/montserrat-cyrillic-500-normal.woff2) format('woff2'), url(https://p.poketube.fun/https://fonts.bunny.net/montserrat/files/montserrat-cyrillic-500-normal.woff) format('woff');
unicode-range: U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116;
}
/* latin-ext */
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-weight: 500;
font-stretch: 100%;
src: url(https://p.poketube.fun/https://fonts.bunny.net/montserrat/files/montserrat-latin-ext-500-normal.woff2) format('woff2'), url(https://p.poketube.fun/https://fonts.bunny.net/montserrat/files/montserrat-latin-ext-500-normal.woff) format('woff');
unicode-range: U+0100-02AF,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
/* vietnamese */
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-weight: 500;
font-stretch: 100%;
src: url(https://p.poketube.fun/https://fonts.bunny.net/montserrat/files/montserrat-vietnamese-500-normal.woff2) format('woff2'), url(https://p.poketube.fun/https://fonts.bunny.net/montserrat/files/montserrat-vietnamese-500-normal.woff) format('woff');
unicode-range: U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB;
}
/* cyrillic-ext */
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-weight: 500;
font-stretch: 100%;
src: url(https://p.poketube.fun/https://fonts.bunny.net/montserrat/files/montserrat-cyrillic-ext-500-normal.woff2) format('woff2'), url(https://p.poketube.fun/https://fonts.bunny.net/montserrat/files/montserrat-cyrillic-ext-500-normal.woff) format('woff');
unicode-range: U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F;
}
:root { :root {
/* text */ /* text */
--text-link: #0ab7f0; --text-link: #0ab7f0;
@ -200,22 +252,29 @@ a.avatar {
.recommended-list { .recommended-list {
background-color: var(--div-prim-bg); background-color: var(--div-prim-bg);
border-radius: 1.5em; border-radius: 1.5em;
/* padding-right: 24px; */
margin: 10px; margin: 10px;
margin-top: 0px; margin-top: 0;
margin-left: 0px; margin-left: 0;
height: -moz-fit-content; height: -moz-fit-content;
height: fit-content; height: fit-content;
justify-self: center; /* justify-self: center; */
margin-right: -0.9em; margin-right: -.9em;
/* width: min-content;*/
border: var(--div-border-color); border: var(--div-border-color);
border-style: solid; border-style: solid;
max-width: 371px; max-width: 371px;
width: max-content; width: 20.9em;
} }
.video-views { .video-views {
white-space: nowrap; white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
white-space: pre-wrap; /* css-3 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
white-space: -webkit-pre-wrap; /* Newer versions of Chrome/Safari*/
word-break: break-all;
white-space: normal;
} }
.video-info-panel.gradient { .video-info-panel.gradient {
@ -234,7 +293,6 @@ a.avatar {
font-weight: 1000; font-weight: 1000;
font-stretch: ultra-expanded; font-stretch: ultra-expanded;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 3; -webkit-line-clamp: 3;
line-clamp: 3; line-clamp: 3;
@ -402,7 +460,7 @@ a.avatar {
border-radius: 4px; border-radius: 4px;
word-break: break-all; word-break: break-all;
white-space: nowrap; white-space: nowrap;
font-family: ubuntu, sans-serif; font-family: "Montserrat", sans-serif;
} }
.new-button { .new-button {
@ -513,7 +571,7 @@ a.avatar {
margin: 0; margin: 0;
width: 300px; width: 300px;
border-radius: 8px; border-radius: 8px;
font-family: ubuntu, sans-serif; font-family: "Montserrat", sans-serif;
box-shadow: var(--border-color) 0 0 5px; box-shadow: var(--border-color) 0 0 5px;
background-color: var(--context-menu-background); background-color: var(--context-menu-background);
} }
@ -523,10 +581,12 @@ a.avatar {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
padding: 0 16px; padding: 0 16px;
padding-left: 16px;
height: 40px; height: 40px;
column-gap: 16px; column-gap: 16px;
color: var(--text-primary); color: var(--text-primary);
text-decoration: none; text-decoration: none;
font-weight: 500;
} }
.dropdown__item:hover { .dropdown__item:hover {
@ -679,7 +739,7 @@ a.new-button:hover {
border-radius: 10px; border-radius: 10px;
height: fit-content; height: fit-content;
padding: 10px; padding: 10px;
font-family: ubuntu, sans-serif; font-family: "ubuntu", sans-serif;
margin-left: -11em; margin-left: -11em;
width: 43em; width: 43em;
position: absolute; position: absolute;
@ -779,7 +839,6 @@ object-fit:none;
font-stretch: expanded; font-stretch: expanded;
overflow:hidden; overflow:hidden;
font-family: var(--text-font-primary); font-family: var(--text-font-primary);
margin-left: auto;
margin-right: auto; margin-right: auto;
width: auto; width: auto;
max-width: 21em; max-width: 21em;
@ -794,7 +853,9 @@ object-fit:none;
} }
.video > .info { .video > .info {
font-family: Ubuntu, sans-serif; font-family: "Montserrat", sans-serif;
font-weight: 500;
} }
/* Width */ /* Width */

View file

@ -94,8 +94,3 @@ a {
</body> </body>
</html> </html>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js');
}
</script>

135
html/apps.ejs Normal file
View file

@ -0,0 +1,135 @@
<!--
This Source Code Form is subject to the terms of the GNU General Public License:
Copyright (C) 2021-2024 PokeTube (https://codeberg.org/Ashley/poketube)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see https://www.gnu.org/licenses/.
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Poke Apps</title>
<meta content="▶▶ Poke - The Ultimate privacy App!" property=og:title>
<meta content="Poke is more then a youtueb front end - see all the stuff in poke!! :3" property=twitter:description>
<meta content="https://cdn.glitch.global/d68d17bb-f2c0-4bc3-993f-50902734f652/aa70111e-5bcd-4379-8b23-332a33012b78.image.png?v=1701898829884" property="og:image" />
<meta content="summary_large_image" name="twitter:card" />
<link rel="manifest" href="/manifest.json">
<link href="/css/yt-ukraine.svg?v=3" rel="icon" />
<link href="https://p.poketube.fun/https://site-assets.fontawesome.com/releases/v6.1.1/css/all.css" rel="stylesheet" />
<style>
@font-face {
font-family: "PokeTube Flex";
src: url("https://p.poketube.fun/https://cdn.glitch.global/43b6691a-c8db-41d4-921c-8cf6aa0d9108/robotoflex.ttf?v=1668343428681");
font-style: normal;
font-stretch: 1% 800%;
font-display: swap;
}
body {
font-family: "PokeTube flex", sans-serif;
margin: 0;
display: flex;
background-image: radial-gradient(
circle,
#231638,
#2b160e,
#09250e,
#0f132b
);
animation: gradient 64s ease infinite;
background-size: 400% 400%;
min-height: 100vh;
align-items: center;
justify-content: center;
}
h1 {
margin-top: 0;
font-weight: 1000;
font-stretch: ultra-expanded;
font-family: "Poketube flex";
color: #fff;
}
@keyframes gradient {
0% {
background-position: 0 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0 50%;
}
}
.poke-container {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
padding: 16px;
background: #1e1e1e;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.poke-header {
text-align: center;
padding: 16px;
background: #212121;
color: #fff;
border-radius: 8px 8px 0 0;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
grid-column: span 4;
}
.subtext {
color: #b0b0b0;
margin-top: 8px;
}
.poke-app-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
background: #2c2c2c;
color: #fff;
text-decoration: none;
border-radius: 4px;
font-size: 14px;
transition: background 0.3s ease, color 0.3s ease, box-shadow 0.3s ease;
}
.poke-app-btn:hover {
background: #404040;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
}
.poke-app-btn i {
margin-bottom: 8px;
font-size: 24px;
}
</style>
</head>
<body>
<div class="poke-container">
<div class="poke-header">
<h1>Moar from Poke</h1>
<p class="subtext">Poke is not just a youtube front end - its moar!!</p>
</div>
<a href="/game-hub" class="poke-app-btn"><i class="fa-light fa-gamepad"></i>Games</a>
<a href="/web" class="poke-app-btn"><i class="fa-light fa-search"></i>Web Search</a>
<a href="/translate" class="poke-app-btn"><i class="fa-light fa-language"></i>Translate</a>
<a href="/map" class="poke-app-btn"><i class="fa-light fa-map-marker-alt"></i>Maps</a>
<a href="/app" class="poke-app-btn"><i class="fa-light fa-play"></i>PokeTube</a>
<a href="/settings" class="poke-app-btn"><i class="fa-light fa-cogs"></i>Settings</a>
</div>
</body>
</html>

View file

@ -1,9 +1,9 @@
<% try { %> <% try { %>
<!-- <!--
This Source Code Form is subject to the terms of the GNU General Public License: This Source Code Form is subject to the terms of the GNU General Public License:
Copyright (C) 2021-2023 POKETUBE (https://codeberg.org/Ashley/poketube) Copyright (C) 2021-2024 POKETUBE (https://codeberg.org/Ashley/poketube)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -19,26 +19,24 @@
along with this program. If not, see https://www.gnu.org/licenses/. along with this program. If not, see https://www.gnu.org/licenses/.
--> -->
<!DOCTYPE html><html><head> <!DOCTYPE html><html><head>
<!-- 🐷 🎗️ -->
<% if (ID === "UCFAiFyGs6oDiF1Nf-rRJpZA") { %> <% if (ID === "UCFAiFyGs6oDiF1Nf-rRJpZA") { %>
<title>Technoblade Never Dies! - PokeTube</title> <title>Technoblade Never Dies! - PokeTube</title>
<% } %> <% } %>
<title><%=j.Channel?.Metadata.Name%> - PokeTube</title> <title><%=j.Channel?.Metadata.Name%> - PokeTube</title>
<link rel="manifest" href="/manifest.json"> <link rel="manifest" href="/manifest.json">
<link href=css/yt-ukraine.svg rel=icon> <link href="css/yt-ukraine.svg" rel=icon>
<meta content=website property=og:type> <meta content=website property=og:type>
<link rel="alternate" type="application/rss+xml" href="/feeds/videos.xml?channel_id=<%=ID%>">
<meta content="<%=j.Channel?.Metadata.Name%> - PokeTube" property=og:title> <meta content="<%=j.Channel?.Metadata.Name%> - PokeTube" property=og:title>
<link href=/css/yt-ukraine.svg?v=6 rel=icon>
<meta content="<%- cinv.description %>" property=twitter:description> <meta content="<%- cinv.description %>" property=twitter:description>
<meta name="darkreader-lock"> <!-- tells dark reader that the site has a dark theme and to turn itself off -->
<% if (j.Channel?.Metadata.Banners.Thumbnail) { %> <% if (j.Channel?.Metadata.Banners.Thumbnail) { %>
<meta content="<%=j.Channel?.Metadata?.Banners.Thumbnail[2].$t%>" property=og:image> <meta content="<%=j.Channel?.Metadata?.Banners.Thumbnail[2].$t%>" property=og:image>
<% } %> <% } %>
<meta content=summary_large_image name=twitter:card> <meta content=summary_large_image name=twitter:card>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<style> <style>
* { * {
color:#fff color:#fff
@ -310,7 +308,56 @@ text-transform:uppercase;
</style> </style>
<% if(!isMobile) { %> <% if(!isMobile) { %>
<noscript>
<style> <style>
#search {
display: none;
}
.subscribe-button.needs-js {
display: none;
}
.subscribe-button {
margin-right: 6px !important;
margin-top: 10px !important;
margin-left: -4px !important;
}
</style>
</noscript>
<style>
.channel-info > .avatar > img {
width: auto;
height: auto;
max-height: 150px;
}
@media screen and (max-width: 1230px) {
.channel-info > .avatar > img {
margin-left: 1em;
}
.name {
margin-left: 5em !important;
}
}
.subscribe-button:hover {
animation: animateBg 2s infinite linear;
background-color: #fff;
background-image: linear-gradient(90deg,#da3287,#ffde5e,#da3287,#ffde5e);
background-size: 300% 100%;
box-shadow: 0 3px 14px #000;
cursor: pointer;
}
@keyframes animateBg {
0% {
background-position:0 0
}
100% {
background-position:100% 0
}
}
.video:hover{ .video:hover{
border:solid; border:solid;
} }
@ -327,6 +374,30 @@ text-transform:uppercase;
} }
.subs { .subs {
margin: 0.6em;text-align: left;margin-left: 0px;margin-top: -1em;font-family: "PokeTube flex";font-weight: 1000;font-stretch: ultra-expanded; margin: 0.6em;text-align: left;margin-left: 0px;margin-top: -1em;font-family: "PokeTube flex";font-weight: 1000;font-stretch: ultra-expanded;
}
#popup-container {
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #111;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
padding: 20px;
border-radius: 8px;
z-index: 1;
width: 45em;
height: 24em;
overflow:auto;
}
#close-btn {
cursor: pointer;
position: absolute;
top: 10px;
right: 10px;
font-size: 18px;
color: #555;
} }
.video-grid { .video-grid {
display: flex; display: flex;
@ -404,6 +475,8 @@ color:#ea9999 !important;
.channel-info{ .channel-info{
text-align:center; text-align:center;
} }
.sticky-top { .sticky-top {
position: sticky; position: sticky;
top: 0px; top: 0px;
@ -494,10 +567,8 @@ color:#ea9999 !important;
<button class="btn btn-success" type="submit"><i class="fa-light fa-search"></i></button> <button class="btn btn-success" type="submit"><i class="fa-light fa-search"></i></button>
</form> </form>
<img src="https://search-metrics.poketube.fun/t/rep.gif" style="border:0;width: 0;visibility: hidden;">
</div> </div>
<img src="https://search-metrics.poketube.fun/t/rep.gif" style="border:0;width: 0;visibility: hidden;">
</div> </div>
<div class="right"> <div class="right">
@ -514,6 +585,20 @@ color:#ea9999 !important;
<% } %> <% } %>
<% if (!isMobile) { %>
<div onclick="closePopup()" id="popup-container">
<div id="close-btn" onclick="closePopup()">X</div>
<% if (cinv.descriptionHtml) { %>
<p style="color:#fff;margin-left: 10px;font-weight: bold;"><%-cinv.descriptionHtml%></p>
</div>
<% } %>
<% } %>
<% if (isMobile) { %> <% if (isMobile) { %>
@ -555,56 +640,50 @@ color:#ea9999 !important;
<img src="https://p.poketube.fun/<%=j.Channel?.Metadata.Banners.Thumbnail[2].$t%>"> <img src="https://p.poketube.fun/<%=j.Channel?.Metadata.Banners.Thumbnail[2].$t%>">
<% } %> <% } %>
<div class="channel-info" >
<div class="channel-info" style="margin-bottom: 2em;margin-left:3em">
<a href="/avatars/<%=j.Channel?.Metadata.Avatars.Thumbnail?.$t.replace("https://yt3.googleusercontent.com/", "")%>" class="avatar"> <a href="/avatars/<%=j.Channel?.Metadata.Avatars.Thumbnail?.$t.replace("https://yt3.googleusercontent.com/", "")%>" class="avatar">
<img src="/avatars/<%=j.Channel?.Metadata.Avatars.Thumbnail?.$t.replace("https://yt3.googleusercontent.com/", "")%>" alt="Channel Avatar"> <img src="/avatars/<%=j.Channel?.Metadata.Avatars.Thumbnail?.$t.replace("https://yt3.googleusercontent.com/", "")%>" alt="Channel Avatar">
</a> </a>
<% if (cinv?.authorVerified) { %> <% if (cinv?.authorVerified) { %>
<style> <style>
.name { .name {
border:solid 2.1px #df80ff; border:solid 1px #df80ff;
} }
</style> </style>
<% } %> <% } %>
<div class="name" style="background: #333;border-radius: 12px;padding: 9px;height: 6em;"> <div class="name" style="background: #333;border-radius: 12px;padding: 17px;height: 7em;margin-left: 4em;margin-top: 7px;gap: 3px;">
<p style="font-family:PokeTube Flex,sans-serif;font-weight:1000;font-stretch: ultra-expanded;margin-top: 16px;margin-bottom: 15px;"><%=j.Channel?.Metadata.Name%> <p style="font-family:PokeTube Flex,sans-serif;font-weight:1000;font-stretch: ultra-expanded;margin-top: 16px;margin-bottom: 15px;"><%=j.Channel?.Metadata.Name%>
<span style="background: #0005;padding: 3px;padding-top: 2.5px !important;display: inline-flex;border-radius: 3px;"> <% if (cinv?.authorVerified) { %><span style="background: #0005;padding: 3px;padding-top: 2.5px !important;display: inline-flex;border-radius: 3px;">
<% if (cinv?.authorVerified) { %>
<% if (cinv?.isFamilyFriendly) { %>
<i class="fa-solid fa-badge-check" style="width: 18px;margin-right:7px" title="Verified Channel"></i>
<i class="fa-solid fa-badge-check" style="width: 18px;margin-right:1px" title="Verified Channel"></i>
</span>
<% } %> <% } %>
<% if (!cinv.isFamilyFriendly) { %> <span style="font-size: 10px;color: gray;">@<%- cinv.authorId %>@youtube.com</span>
<i class="fa-solid fa-badge-check" style="width: 18px" title="Verified Channel"></i> </p>
<p style="margin-bottom: -17px;" class="subs"><%=subs%> subscribers</p>
<% } %>
<% } %>
<% if (cinv?.isFamilyFriendly) { %>
<img src="https://cdn.glitch.global/d68d17bb-f2c0-4bc3-993f-50902734f652/bbf42a35-8976-43a5-a7a2-72a81d9da7ce.image.png?v=1692301199470" style="width: 18px;" title="In Youtube kids" >
<% } %>
</span> </p>
<p style="margin-bottom: -11px;" class="subs"><%=subs%> subscribers</p>
<p style="padding:0;font-weight:bold;max-inline-size: 37em;display: -webkit-box;-webkit-line-clamp: 3;-webkit-box-orient: vertical;"> <p style="padding:0;font-weight:bold;max-inline-size: 37em;display: -webkit-box;-webkit-line-clamp: 3;-webkit-box-orient: vertical;">
<% try { %> <% try { %>
<%- getFirstLine(cinv.description).slice(0, 60) %> <%- getFirstLine(cinv.description).slice(0, 60) || "More from this channel (soon)"%>
<a href="/channel?id=<%= ID %>&tab=about"> <a href="/channel?id=<%=ID%>&tab=about&legacy=true" id="popup-trigger">
<% } catch (error) { %> <% } catch (error) { %>
<!-- Handle the error here, if it occurs --> <!-- Handle the error here, if it occurs -->
<p>Error: <%= error.message %></p> <p>Error: <%= error.message %></p>
<% } %> <% } %>
<i class="fa-thin fa-angle-right"></i></a><br><button class="subscribe-button " style="text-decoration: none;margin-right:3px !important;"> <a style="color: black; <i class="fa-thin fa-angle-right"></i></a><br><button class="subscribe-button needs-js" style="text-decoration: none;margin-right:6px !important;margin-top: 10px !important;margin-left: -4px !important;"> <a style="color: black;
white-space: nowrap;text-decoration: none;" id="sub">Suscribe</a></button><button class="subscribe-button " style="text-decoration: none;"> <a style="color: black; white-space: nowrap;text-decoration: none;" id="sub">Suscribe</a></button><button class="subscribe-button " style="text-decoration: none;"> <a style="color: black;
white-space: nowrap;text-decoration: none;" href="/feeds/videos.xml?channel_id=<%=ID%>">Rss feed </a></button> white-space: nowrap;text-decoration: none;" href="/feeds/videos.xml?channel_id=<%=ID%>">Rss feed </a></button>
@ -652,8 +731,62 @@ white-space: nowrap;text-decoration: none;" href="/feeds/videos.xml?channel_id=<
<% } %> <% } %>
<% } %> <% } %>
<% if (Array.isArray(playlist?.playlists)) { %>
<% if (playlist.playlists.length != "0") { %>
<a href="/channel?id=<%=ID%>&tab=playlist" class="tab" style="color:pink">Playlists</a>
<% } %>
<% } %>
<% } %> <% } %>
<% if (tab === "playlist") { %>
<a href="/channel?id=<%=ID%>" class="tab" style="color:#cfe2f3;">Videos</a>
<% if (Array.isArray(shorts?.videos)) { %>
<% if (shorts.videos[0]) { %>
<% if (turntomins(shorts.videos[0].lengthSeconds) != "aN:aN" ) { %>
<a href="/channel?id=<%=ID%>&tab=shorts" class="tab shr">Shorts</a>
<% } %>
<% } %>
<% } %>
<% if (Array.isArray(stream?.videos)) { %>
<% if (stream.videos[0]) { %>
<% if (turntomins(stream.videos[0].lengthSeconds) != "aN:aN" ) { %>
<a href="/channel?id=<%=ID%>&tab=live" class="tab" style="color:#d9ead3;">Live</a>
<% } %>
<% } %>
<% } %>
<% if (Array.isArray(c?.comments)) { %>
<% if (c.comments.length != "0") { %>
<a href="/channel?id=<%=ID%>&tab=community" class="tab" style="color:pink">Community</a>
<% } %>
<% } %>
<% if (Array.isArray(playlist?.playlists)) { %>
<% if (playlist.playlists.length != "0") { %>
<a href="/channel?id=<%=ID%>&tab=playlist" class="tab active" style="color:pink">Playlists</a>
<% } %>
<% } %>
<% } %>
<% if (tab === "about") { %> <% if (tab === "about") { %>
<a href="/channel?id=<%=ID%>" class="tab" style="color:#cfe2f3;">Videos</a> <a href="/channel?id=<%=ID%>" class="tab" style="color:#cfe2f3;">Videos</a>
@ -711,6 +844,15 @@ white-space: nowrap;text-decoration: none;" href="/feeds/videos.xml?channel_id=<
<a href="/channel?id=<%=ID%>&tab=community" class="tab active" style="color:pink">Community</a> <a href="/channel?id=<%=ID%>&tab=community" class="tab active" style="color:pink">Community</a>
<% if (Array.isArray(playlist?.playlists)) { %>
<% if (playlist.playlists.length != "0") { %>
<a href="/channel?id=<%=ID%>&tab=playlist" class="tab" style="color:pink">Playlists</a>
<% } %>
<% } %>
<% } %> <% } %>
<% if (tab === "shorts") { %> <% if (tab === "shorts") { %>
@ -738,6 +880,15 @@ white-space: nowrap;text-decoration: none;" href="/feeds/videos.xml?channel_id=<
<% } %> <% } %>
<% } %> <% } %>
<% if (Array.isArray(playlist?.playlists)) { %>
<% if (playlist.playlists.length != "0") { %>
<a href="/channel?id=<%=ID%>&tab=playlist" class="tab" style="color:pink">Playlists</a>
<% } %>
<% } %>
<% } %> <% } %>
<% if (tab === "live") { %> <% if (tab === "live") { %>
@ -765,6 +916,7 @@ white-space: nowrap;text-decoration: none;" href="/feeds/videos.xml?channel_id=<
<% } %> <% } %>
<% } %> <% } %>
</div> </div>
<input type="text" id="search" placeholder="Search :3" style="margin-top: 2em;height: 1em;color: #fff;background: #111;border-radius: 1em;padding: 7px;margin-left: auto;margin-right: 1em;"> </div> <input type="text" id="search" placeholder="Search :3" style="margin-top: 2em;height: 1em;color: #fff;background: #111;border-radius: 1em;padding: 7px;margin-left: auto;margin-right: 1em;"> </div>
@ -1110,7 +1262,7 @@ width: fit-content;
<% if (Array?.isArray( shorts.videos)) { %> <% if (Array?.isArray( shorts.videos)) { %>
<% shorts.videos.forEach (x => { %> <% shorts.videos.forEach (x => { %>
<a href="/shorts/<%- x.videoId %>" class="shorts-video" > <a href="/shorts/<%- x.videoId %>" class="shorts-video" >
<img load="lazy" src='<%- media_proxy_url %>/proxy?url=https://vid.puffyan.us/vi/<%= x.videoId %>/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLBy_x4UUHLNDZtJtH0PXeQGoRFTgw'> <img load="lazy" onerror="this.src=`<%- media_proxy_url %>/proxy?url=https://vid.puffyan.us/vi/<%= x.videoId %>/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLBy_x4UUHLNDZtJtH0PXeQGoRFTgw`" src='<%- media_proxy_url %>/proxy?url=https://vid.puffyan.us/vi/<%= x.videoId %>/maxresdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLBy_x4UUHLNDZtJtH0PXeQGoRFTgw'>
<span class="shorts-title"><%- x.title %></span> <span class="shorts-title"><%- x.title %></span>
</a> </a>
<% }) %> <% }) %>
@ -1123,7 +1275,16 @@ width: fit-content;
</div> </div>
<% } %> <% } %>
<style> <!-- SHORTS-VIDEO --->
<!-- SHORTS-VIDEO --->
<!-- SHORTS-VIDEO --->
<!-- SHORTS-VIDEO --->
<!-- SHORTS-VIDEO --->
<!-- SHORTS-VIDEO --->
<!-- SHORTS-VIDEO --->
<!-- SHORTS-VIDEO --->
<style nonce="IJD3y0awTwA2dd0pWOP+ZQ">
.shorts-video-grid { .shorts-video-grid {
max-width: 1200px; max-width: 1200px;
margin: auto; margin: auto;
@ -1143,23 +1304,14 @@ width: fit-content;
} }
</style> </style>
<!-- SHORTS-VIDEO --->
<!-- SHORTS-VIDEO --->
<!-- SHORTS-VIDEO --->
<!-- SHORTS-VIDEO --->
<!-- SHORTS-VIDEO --->
<!-- SHORTS-VIDEO --->
<!-- SHORTS-VIDEO --->
<!-- SHORTS-VIDEO --->
<% } %> <% } %>
@ -1237,6 +1389,40 @@ width: fit-content;
<% } %> <% } %>
<% if (tab === "playlist") { %>
<div align="center">
<div class="video-grid" >
<% if (Array.isArray(playlist.playlists)) { %>
<% playlist.playlists.forEach (x => { %>
<a href="/playlist?list=<%- x.playlistId %>" class="video">
<div class="thumbnail" style="background-image: url('<%- media_proxy_url %>/proxy?url=<%- x.playlistThumbnail %>?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLBy_x4UUHLNDZtJtH0PXeQGoRFTgw');border-radius: 10px;"><span class="video-length"><%- x.videoCount %> Videos </span></div>
<div class="info">
<span class="title max-lines-2" style="font-family:PokeTube flex,sans-serif;font-weight: 1000;font-stretch: ultra-expanded;"><%- x.title %></span>
</div>
</a>
<% }) %>
<% } %>
</div>
<% } %>
<% if (tab === "community") { %> <% if (tab === "community") { %>
<% if (Array?.isArray( c?.comments)) { %> <% if (Array?.isArray( c?.comments)) { %>
@ -1320,39 +1506,38 @@ width: fit-content;
<% if (tab === "about") { %> <% if (tab === "about") { %>
<div style="text-align: left;">
<h3 style="color:#fff;font-family:Ginto Nord,sans-serif;font-weight:900;padding: 0px;margin: 0;margin-top: 7px;margin-left: 10px;">About</h3>
<% if (!isMobile) { %>
<div style="display: flex;flex-direction: row;">
<div style="background: #333;display: flex;flex-direction: column;margin-top: 1em;margin-left: 1em;border-radius: 1em;margin-bottom: 1em;box-sizing: border-box;width: 100%;margin-right: 20px;min-height: 19em;">
<h3 style="color:#fff;font-family:PokeTube flex,sans-serif;font-weight:1000;font-stretch:ultra-expanded:padding: 0px;margin: 0;margin-top: 7px;margin-left: 10px;">About</h3>
<% if (cinv.descriptionHtml) { %> <% if (cinv.descriptionHtml) { %>
<p style="color:#fff;margin-left: 10px;font-weight: bold;"><%-cinv.descriptionHtml%></p> <p style="color:#fff;margin-left: 10px;font-weight: bold;"><%-cinv.descriptionHtml%></p>
<% } %> <% } %>
<% if (wiki.extract_html) { %>
<h3 style="color:#fff;font-family:Ginto Nord,sans-serif;font-weight:900;padding: 0px;margin: 0;margin-top: 7px;margin-left: 10px;">From the web</h3>
<div style="color:#fff;margin-left: 10px;">
<p style="color:#fff:font-weight: bold;">
<%-wiki.extract_html%> </p>
</div> </div>
<div style="color:#fff;margin-left: 10px;">
<p style="margin-bottom: 10px;">
<a href=" <%-wiki.content_urls.desktop.page%>
">From wikipedia </a> under CC-BY-SA 3.0
</p>
</div>
</p>
<% } %> <% } %>
<div style="font-weight: bold;color:#fff;margin-left: 10px;">
YouTube removed the about tab ;_; fix for this soon lol <% if (isMobile) { %>
</div>
<div style="text-align: left;">
<h3 style="color:#fff;font-family:Ginto Nord,sans-serif;font-weight:900;padding: 0px;margin: 0;margin-top: 7px;margin-left: 10px;">About</h3>
<% if (cinv.descriptionHtml) { %>
<p style="color:#fff;margin-left: 10px;font-weight: bold;"><%-cinv.descriptionHtml%></p>
<% } %>
<% } %>
</div> </div>
<% } %> <% } %>
@ -1370,7 +1555,7 @@ if (userID) {
anchor.href = `/api/set-channel-subs?ID=${userID}&channelName=<%=j.Channel?.Metadata.Name%>&avatar=https://p.poketube.fun/<%- j.Channel?.Metadata.Avatars.Thumbnail?.$t %>&channelID=<%= ID %>`; anchor.href = `/api/set-channel-subs?ID=${userID}&channelName=<%=j.Channel?.Metadata.Name%>&avatar=https://p.poketube.fun/<%- j.Channel?.Metadata.Avatars.Thumbnail?.$t %>&channelID=<%= ID %>`;
} else { } else {
// If user ID doesn't exist in localStorage, you can handle it as needed // If user ID doesn't exist in localStorage, you can handle it as needed
console.log("User ID not found in localStorage"); anchor.href = "/account-create"
// Optionally, you can set a default href or display an error message. // Optionally, you can set a default href or display an error message.
} }
@ -1405,7 +1590,28 @@ document.getElementById('search').addEventListener('keyup', function () {
} }
}); });
}); });
var isPopupOpen = false;
function togglePopup() {
if (isPopupOpen) {
closePopup();
} else {
document.getElementById('popup-container').style.display = 'block';
document.body.style.overflow = 'hidden';
}
isPopupOpen = !isPopupOpen;
}
function closePopup() {
document.getElementById('popup-container').style.display = 'none';
document.body.style.overflow = 'auto';
isPopupOpen = false;
}
document.getElementById('popup-trigger').addEventListener('click', function (event) {
event.preventDefault();
togglePopup();
});
// @license-end // @license-end
</script> </script>
@ -1417,8 +1623,4 @@ document.getElementById('search').addEventListener('keyup', function () {
<% } catch (error) { %> <% } catch (error) { %>
<% } %> <% } %>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js');
}
</script>

View file

@ -1,7 +1,7 @@
<!-- <!--
This Source Code Form is subject to the terms of the GNU General Public License: This Source Code Form is subject to the terms of the GNU General Public License:
Copyright (C) 2021-2023 POKETUBE (https://codeberg.org/Ashley/poketube) Copyright (C) 2021-2024 POKETUBE (https://codeberg.org/Ashley/poketube)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -31,13 +31,12 @@
<meta content=@PoketaleBot name=twitter:site> <meta content=@PoketaleBot name=twitter:site>
<meta content=@PoketaleBot name=twitter:creator> <meta content=@PoketaleBot name=twitter:creator>
<link rel="manifest" href="/manifest.json"> <link rel="manifest" href="/manifest.json">
<meta name="darkreader-lock"> <!-- tells dark reader that the site has a dark theme and to turn itself off -->
<link href=/css/app-cdn.min.css rel=stylesheet> <link href=/css/app-cdn.min.css rel=stylesheet>
<link href=/css/app-cdn.min.css rel=stylesheet> <link href=/css/app-cdn.min.css rel=stylesheet>
<link href=/css/app.main.css?v=45 rel=stylesheet> <link href=/css/app.main.css?v=45 rel=stylesheet>
<link href=/css/search.main.css rel=stylesheet> <link href=/css/search.main.css rel=stylesheet>
<link href=/css/watch.main.css rel=stylesheet> <link href=/css/watch.main.css rel=stylesheet>
<link href="https://fonts.poketube.fun/css/fonts.css" rel=stylesheet>
<meta content="#1a1a1a" name="theme-color"> <meta content="#1a1a1a" name="theme-color">
</head> </head>
<style> <style>
@ -519,6 +518,10 @@ Discover Popular videos on poketube!
<script> <script>
// @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-3.0-or-later // @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-3.0-or-later
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js');
}
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
let bgs = document.querySelectorAll('[data-bg]'); let bgs = document.querySelectorAll('[data-bg]');
let bgCount = bgs.length; let bgCount = bgs.length;
@ -1319,8 +1322,24 @@ links.forEach(link => {
}</style> }</style>
<% } %> <% } %>
<script> // @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-3.0-or-later
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(function(registrations) {
for (let registration of registrations) {
registration.unregister();
}
});
if ('caches' in window) {
caches.keys().then(function(cacheNames) {
cacheNames.forEach(function(cacheName) {
caches.delete(cacheName);
});
});
}
}
</script>
</body > </body >
</html> <% } %> </html> <% } %>

1111
html/gamehub.ejs Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -3,7 +3,7 @@
This Source Code Form is subject to the terms of the GNU General Public License: This Source Code Form is subject to the terms of the GNU General Public License:
Copyright (C) 2021-2023 POKETUBE (https://codeberg.org/Ashley/poketube) Copyright (C) 2021-2024 POKETUBE (https://codeberg.org/Ashley/poketube)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -557,32 +557,68 @@ background: none !important;
</div> </div>
<% if (!f) { %> <% if (!f) { %>
<% k.Video.Recommendations.Video.forEach(x => { %>
<div class="video" >
<% if (!optout) { %>
<a href="/lite?v=<%= x.id %>" class="thumbnail" style="background-image: url('/vi/<%= x.id %>/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLBy_x4UUHLNDZtJtH0PXeQGoRFTgw');border-radius: 9.5px;" > <span class="video-length"><%=x.duration || "LIVE" %></span> <% if (inv_vid.recommendedVideos) { %>
<% } %>
<% if (optout) { %>
<a href="/lite?v=<%= x.id %>&t=f" class="thumbnail" style="background-image: url('/vi/<%= x.id %>/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLBy_x4UUHLNDZtJtH0PXeQGoRFTgw');border-radius: 9.5px;" > <span class="video-length"><%- x.duration || "LIVE"%></span> <% inv_vid?.recommendedVideos.forEach(x => { %>
<% } %> <div class="fade-in video">
</a> <% if (!optout) { %><a class="thumbnail" href="/lite?v=<%= x.videoId %>" style="background-image:url(<%- media_proxy_url %>/proxy?url=https://vid.puffyan.us/vi/<%= x.videoId %>/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLBy_x4UUHLNDZtJtH0PXeQGoRFTgw);border-radius:9.5px" alt="<%= x.Title %>"><span class="video-length"><%- turntomins(x.lengthSeconds) || "LIVE"%></span><% } %><% if (optout) { %><a class="thumbnail"href="/lite?v=<%= x.videoId %>&m=f"style="background-image:url(<%- media_proxy_url %>/proxy?url=https://vid.puffyan.us/vi/<%= x.videoId %>/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLBy_x4UUHLNDZtJtH0PXeQGoRFTgw);border-radius:9.5px"alt="<%= x.Title %>"><span class="video-length"><%- x.duration || "LIVE"%></span><% } %></a>
<div class="info"> <div class="info">
<% if (!optout) { %> <% if (!optout) { %>
<a href="/lite?v=<%= x.id %>" class="title max-lines-2" title="<%= x.Title %>" style="font-stretch: 100%;font-weight: 800;"><%= x.Title %></a> <a class="max-lines-2 title" href="/lite?v=<%= x.videoId %>" style="font-stretch:ultra-expanded;font-weight:850" title="<%= x.Title %>">
<%= x.title %>
</a>
<% } %> <% } %>
<% if (optout) { %> <% if (optout) { %>
<a href="/lite?v=<%= x.id %>&t=f" class="title max-lines-2" title="<%= x.Title %>" style="font-stretch: 100%;font-weight: 800;"><%= x.Title %></a> <a class="max-lines-2 title" href="/lite?v=<%= x.videoId %>&m=f" style="font-stretch:100%;font-weight:800" title="<%= x.Title %>">
<%= x.title %>
</a>
<% } %> <% } %>
<div> <div>
<a class="max-lines-2" href="/channel?id=<%= x.Channel.id %>" style="-webkit-line-clamp: 1;width: 12em;word-wrap: break-word;"><%=x.Channel.Name %></a> <a class="max-lines-2" href="/channel?id=<%= x.authorId %>@youtube.com" style="-webkit-line-clamp:1;width:12em;word-wrap:break-word">
<div class="video-views"> <%= x.uploadedAt.replace("Streamed", "Live") %> • <%= convert(x.views) %> views </div> <%=x.author %>
</div> </a>
</div> <div class="video-views">
</div>
<%= convert(x.viewCount) %> views
</div>
</div>
</div>
</div>
<% }) %>
<% } %>
<% } %>
<% if (inv_vid.recommendedVideos.length < 1) { %>
<% channel_uploads.latestVideos.forEach(x => { %>
<div class="fade-in video">
<% if (!optout) { %><a class="thumbnail" href="/lite?v=<%= x.videoId %>" style="background-image:url(<%- media_proxy_url %>/proxy?url=https://vid.puffyan.us/vi/<%= x.videoId %>/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLBy_x4UUHLNDZtJtH0PXeQGoRFTgw);border-radius:9.5px" alt="<%= x.Title %>"><span class="video-length"><%- turntomins(x.lengthSeconds) || "LIVE"%></span><% } %><% if (optout) { %><a class="thumbnail"href="/lite?v=<%= x.videoId %>&m=f"style="background-image:url(<%- media_proxy_url %>/proxy?url=https://vid.puffyan.us/vi/<%= x.videoId %>/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLBy_x4UUHLNDZtJtH0PXeQGoRFTgw);border-radius:9.5px"alt="<%= x.Title %>"><span class="video-length"><%- x.duration || "LIVE"%></span><% } %></a>
<div class="info">
<% if (!optout) { %>
<a class="max-lines-2 title" href="/lite?v=<%= x.videoId %>" style="font-stretch:ultra-expanded;font-weight:850" title="<%= x.Title %>">
<%= x.title %>
</a>
<% } %>
<% if (optout) { %>
<a class="max-lines-2 title" href="/lite?v=<%= x.videoId %>&m=f" style="font-stretch:100%;font-weight:800" title="<%= x.Title %>">
<%= x.title %>
</a>
<% } %>
<div>
<a class="max-lines-2" href="/channel?id=<%= x.authorId %>@youtube.com" style="-webkit-line-clamp:1;width:12em;word-wrap:break-word">
<%=x.author %>
</a>
<div class="video-views">
<%= convert(x.viewCount) %> views
</div>
</div>
</div>
</div>
<% }) %> <% }) %>
<% } %> <% } %>

265
html/playlist.ejs Normal file
View file

@ -0,0 +1,265 @@
<!--
This Source Code Form is subject to the terms of the GNU General Public License:
Copyright (C) 2021-2024 POKETUBE (https://codeberg.org/Ashley/poketube)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see https://www.gnu.org/licenses/.
--><!DOCTYPE html><html style="background:#000000">
<head>
<title>Poke | <%- p.title %> </title>
<link href=/css/yt-ukraine.svg?v=6 rel=icon>
<link rel="manifest" href="/manifest.json">
<meta content=website property=og:type>
<meta name="viewport" content="width=device-1200px, initial-scale=1.0, shrink-to-fit=yes, viewport-fit=cover">
<meta content="Poke - <%- p.title %> " property=og:title>
<meta content="<%- p.description %>" property=twitter:description>
<meta content="<%- mediaproxy %>/proxy?url=<%- p.playlistThumbnail %>" property="og:image" />
<meta content=summary_large_image name=twitter:card>
<link href=/css/app-cdn.min.css rel=stylesheet>
<link href=/css/app.main.css?v=8 rel=stylesheet>
<link href="/css/watch.main.css?v=56" rel=stylesheet>
<link href=https://p.poketube.fun/https://site-assets.fontawesome.com/releases/v6.1.1/css/all.css rel=stylesheet>
<style>
a.class:hover {
font-weight:bold
}
summary{
color:gray;
}
summary:hove
color:white;
}
</style>
<!-- WIGGLE WIGGLE WIGGLE -->
<style>
body{
overflow-x: hidden; /* Hide horizontal scrollbar */
color:#111111;
}
.animated {
-webkit-animation-duration: 10s;
animation-duration: 10s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
animation-iteration-count: infinite;
-moz-animation-iteration-count: infinite;
-webkit-animation-iteration-count: infinite;
-o-animation-iteration-count: infinite;
}
@-webkit-keyframes wiggle {
0% { -webkit-transform: skewX(9deg); }
10% { -webkit-transform: skewX(-8deg); }
20% { -webkit-transform: skewX(7deg); }
30% { -webkit-transform: skewX(-6deg); }
40% { -webkit-transform: skewX(5deg); }
50% { -webkit-transform: skewX(-4deg); }
60% { -webkit-transform: skewX(3deg); }
70% { -webkit-transform: skewX(-2deg); }
80% { -webkit-transform: skewX(1deg); }
90% { -webkit-transform: skewX(0deg); }
100% { -webkit-transform: skewX(0deg); }
}
@keyframes wiggle {
0% { transform: skewX(9deg); }
10% { transform: skewX(-8deg); }
20% { transform: skewX(7deg); }
30% { transform: skewX(-6deg); }
40% { transform: skewX(5deg); }
50% { transform: skewX(-4deg); }
60% { transform: skewX(3deg); }
70% { transform: skewX(-2deg); }
80% { transform: skewX(1deg); }
90% { transform: skewX(0deg); }
100% { transform: skewX(0deg); }
}
.wiggle {
-webkit-animation-name: wiggle;
animation-name: wiggle;
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
.animated.wiggle {
-webkit-animation-duration: 0.75s;
animation-duration: 0.75s;
}
:root {
--text-primary: #fff;
--text-secondary: #fff;
--text-link: #3ea6ff;
--app-background: #111111;
--context-menu-background: #333;
--border-color: #444;
--item-hover-background: #373737;
--item-active-background: #383838;
--top-bar-background: #202020;
--guide-background: #212121;
--thumbnail-background: #252525;
--channel-info-background: #181818;
--channel-contents-background: #0f0f0f;
}
.alert {
padding: 20px;
background-color: #f44336;
color: white;
opacity: 1;
transition: opacity 0.6s;
margin-bottom: 15px;
}
.alert.success {background-color: #04AA6D;}
.alert.info {background-color: #2196F3;}
.alert.warning {background-color: #ff9800;}
.closebtn {
margin-left: 15px;
color: white;
font-weight: bold;
float: right;
font-size: 22px;
line-height: 20px;
cursor: pointer;
transition: 0.3s;
}
a{
border-radius:13px
}
.video-list > p {
font-family:Ubuntu
}
.closebtn:hover {
color: black;
}
.playlist-video > .info > .title {
border-radius:0em;
max-height: 1.9em;
}
.playlist-info>.title {
font-family: "PokeTube flex";
font-stretch: ultra-expanded;
font-weight: 600;
}
.playlist-info{
position: sticky
}
.playlist-info::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 13em;
height: 447px;
background-size: cover;
filter: blur(53px);
backdrop-filter: blur(157px);
z-index: -1;
background-image: url(<%- mediaproxy %>/proxy?url=<%- p.playlistThumbnail %>?sqp=-oaymwEWCKgBEF5IWvKriqkDCQgBFQAAiEIYAQ==&rs=AOn4CLA9kTQiXKs92q5RXRPYMkL6jKhNJQsqp=-oaymwEXCNACELwBSFryq4qpAwkIARUAAIhCGAE=&amp;rs=AOn4CLCLG7gzsIdtlp7ugZJH8YaAHX5bIw&amp;days_since_epoch=19755);
margin-left: 82px;
}
</style>
</head>
<body>
<nav>
<div class="left"><a class="class" href="/143" style=font-family:Inter,sans-serif;color:#fff> <img style="transform: scale(1.3);padding-left:0.9em;width: 8.5em;display: block;margin-left: auto;margin-right: auto;" src="/css/logo.svg?v=5"> </a> </div>
<div class="middle">
<div class="search">
<form action="/search">
<input class="search-bar" autocomplete="on" id="fname" name="query" placeholder="" style="color:#fff;font-family:Inter,sans-serif;border-radius: 8px;" data-ddg-inputtype="identities.firstName">
<button class="btn btn-success" type="submit"><i class="fa-light fa-search"></i></button>
</form>
</div>
</div>
<div class="right">
<a href="/privacy" style="text-decoration: none;" title="Privacy Policy">
<i style="display: block;margin-left: auto;margin-right: auto;" class="fa-light fa-shield"></i>
</a>
</div>
</nav>
<div class="playlist-page" style="margin-left: auto;margin-right: auto;width: 56em;">
<div class="playlist-info" style="max-height: 26em;border-radius: 1em;">
<% if (!p.mixId) { %>
<div class="thumbnail" style="background-image: url('<%- mediaproxy %>/proxy?url=<%- p.playlistThumbnail %>sqp=-oaymwEXCNACELwBSFryq4qpAwkIARUAAIhCGAE=&amp;rs=AOn4CLCLG7gzsIdtlp7ugZJH8YaAHX5bIw&amp;days_since_epoch=19755');border-radius: 13px;border: 1px solid #ff0064">
</div> <% } %>
<p class="title"><%- p.title %> </p>
<% if (!p.mixId) { %>
<span class="info"><%- p.videoCount %> videos • <%- p?.viewCount?.toLocaleString() %> views • by <%- p.author %> </span>
<% } %>
<span class="description"><%- p.description %></span>
<div class="channel-info">
</div>
</div>
<div class="video-list playlist-video-list">
<% p.videos.forEach(x => { %>
<div class="playlist-video">
<a href="/watch?v=<%- x.videoId %>" class="index">
<%- x.index + 1 %>
</a>
<a href="/watch?v=<%- x.videoId %>" class="thumbnail"
style="background-image: url('<%- mediaproxy %>/proxy?url=https://vid.puffyan.us/vi/<%- x.videoId %>/hqdefault.jpg?sqp=-oaymwEjCNACELwBSFryq4qpAxUIARUAAAAAGAElAADIQj0AgKJDeAE=&amp;rs=AOn4CLBibY6d4Dg0K69NC-0b7NFsD7AH1w');">
</a>
<div class="info">
<a href="/watch?v=<%- x.videoId %>" class="title max-lines-2">
<%- x.title %>
</a>
<div>
<a href="/channel?id=<%- x.authorId %>" style="border-radius:0em"><%- x.author %></a>
</div>
</div>
</div>
<% }) %>
</div>
</div>
</div>
<script src="/css/custom-css.js"> </script>
</body>
</html>

File diff suppressed because it is too large Load diff

78
html/rewind.ejs Normal file
View file

@ -0,0 +1,78 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Poke Rewind</title>
<link href="/css/yt-ukraine.svg?v=6" rel=icon>
<meta content="website" property="og:type">
<meta content="Poke Rewind 2023! - see the year 2023 in poke1" property="twitter:description">
<meta content="Poke Rewind!" property="og:title">
<meta content="https://cdn.glitch.global/d68d17bb-f2c0-4bc3-993f-50902734f652/aa70111e-5bcd-4379-8b23-332a33012b78.image.png?v=1701898829884" property="og:image" />
<meta content="summary_large_image" name="twitter:card">
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
font-family: 'Arial', sans-serif;
background-color: #1a1a1a;
color: #ffffff;
}
img {
height: 2em;
margin-left: auto;
margin-right: auto;
text-align: center;
display: flex;
margin-bottom: 12px;
}
#countdown {
font-size: 36px;
text-align: center;
padding: 20px;
border: 2px solid #444;
border-radius: 10px;
background-color: #333;
box-shadow: 0 0 10px rgba(255, 255, 255, 0.1);
}
</style>
</head>
<body>
<div>
<img src="/css/logo-poke.svg">
<div id="countdown"></div>
</div>
<script>
// @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-3.0-or-later
const countDownDate = new Date("December 28, 2023 00:00:00 UTC").getTime();
const x = setInterval(function() {
const now = new Date().getTime();
const distance = countDownDate - now;
const days = Math.floor(distance / (1000 * 60 * 60 * 24));
const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
document.getElementById("countdown").innerHTML = `${days}d ${hours}h ${minutes}m ${seconds}s`;
// If the countdown is over, display a message
if (distance < 0) {
clearInterval(x);
document.getElementById("countdown").innerHTML = "poke~ Rewind";
}
}, 1000); // Update every 1000 milliseconds
// @license-end
</script>
</body>
</html>

View file

@ -216,14 +216,21 @@
</div> </div>
<div class="search-wrap--home"> <div class="search-wrap--home">
<form name="x" id="search_form_homepage" class="search" action="/web" method="get"> <form name="x" id="search_form_homepage" class="search" action="/web" method="get">
<input placeholder="Search the web" name="query" autocomplete="off" id="search_form_input_homepage" class="search__input" type="text" autofocus /> <input placeholder="Search the web" name="query" autocomplete="off" id="search_form_input_homepage" class="search__input" type="text" autofocus />
<div>
<button class="btn btn-success" type="submit" style="margin-top: 1em;margin-left: auto;text-align: center;display: flex;flex-direction: column;background: #fff;background-color: #303134;border: 1px solid #303134;border-radius: 4px;color: #e8eaed;font-family: arial,sans-serif;font-size: 14px;padding: 0 16px;line-height: 27px;height: 36px;min-width: 54px;margin-right: 13em;text-align: center;cursor: pointer;user-select: none;">
<p style="margin-top: auto;margin-bottom: auto;font-weight: 1000;margin-left: 6px;font-stretch: ultra-expanded;font-family:&quot;poketube flex&quot;;Poketube flex;">Search Poke</p>
</button>
<button class="btn btn-success" type="submit" style="margin-top: 1em;margin-left: auto;margin-right: 8em;align-self: center;text-align: center;display: flex;flex-direction: column;height: 5em;background: #fff;color: #000;border: none;width: 8em;"><p style="margin-top: auto;margin-bottom: auto;font-weight: 1000;margin-left: 6px;font-stretch: ultra-expanded;font-family:&quot;poketube flex&quot;;Poketube flex;">Search Poke</p></button> <button class="btn btn-success" name="lucky" value="true" type="submit" style="margin-top: 1em;margin-left: auto;text-align: center;display: flex;flex-direction: column;background: #fff;background-color: #303134;border: 1px solid #303134;border-radius: 4px;color: #e8eaed;font-family: arial,sans-serif;font-size: 14px;padding: 0 16px;line-height: 27px;height: 36px;min-width: 54px;margin-right: 9em;text-align: center;cursor: pointer;user-select: none;margin-top: -38px;margin-left: 21em;">
<p style="margin-top: auto;margin-bottom: auto;font-weight: 1000;margin-left: 6px;font-stretch: ultra-expanded;font-family:&quot;poketube flex&quot;;Poketube flex;">Im feeling lucky</p>
</button>
</div>
</form>
<div class="downnav"> <div class="downnav">
<span style="color:#fff;" id="weatherInfo"></span> - <a style="color:#fff" href="/map"> PokeMaps </a> - <a style="color:#fff" href="/account-create">My Account</a> - <a style="color:#fff" href="/privacy">Privacy</a> - <a style="color:#fff" href="https://codeberg.org/ashley/poketube">Git</a> <span style="color:#fff;" id="weatherInfo"></span> - <a style="color:#fff" href="/translate"> Translate </a> - <a style="color:#fff" href="/map"> PokeMaps </a> - <a style="color:#fff" href="/account-create">My Account</a> - <a style="color:#fff" href="/privacy">Privacy</a> - <a style="color:#fff" href="https://codeberg.org/ashley/poketube">Git</a>
<div style="float: right;"> <div style="float: right;">
<a style="color:#fff" id="osInfo"></a> <a style="color:#fff;margin-right: 1em;" href="/license">License</a> <a style="color:#fff" id="osInfo"></a> <a style="color:#fff;margin-right: 1em;" href="/license">License</a>
</div> </div>
@ -231,7 +238,6 @@
</form>
<script> <script>
const userAgent = navigator.userAgent.toLowerCase(); const userAgent = navigator.userAgent.toLowerCase();

View file

@ -1,7 +1,7 @@
<!-- <!--
This Source Code Form is subject to the terms of the GNU General Public License: This Source Code Form is subject to the terms of the GNU General Public License:
Copyright (C) 2021-2023 POKETUBE (https://codeberg.org/Ashley/poketube) Copyright (C) 2021-2024 POKETUBE (https://codeberg.org/Ashley/poketube)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -134,7 +134,19 @@ summary:hover{
font-family: ubuntu, sans-serif; font-family: ubuntu, sans-serif;
} }
.search-result{
color: white;
width: 52em;
display: block;
white-space: -moz-pre-wrap !important;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
white-space: pre-wrap;
word-wrap: break-word;
white-space: -webkit-pre-wrap;
word-break: break-all;
white-space: normal;
}
.hide { .hide {
display: none; display: none;
} }
@ -445,17 +457,17 @@ Web
<div class="video-list" > <div class="video-list" >
<% results.forEach(x => { %> <% results.forEach(x => { %>
<div class="video" style="height: 6em;"> <div class="video" style="height: 7em;">
<a style="min-width: 81em;" href="<%= x.link %>"><%= x.title %></a><br> <a style="min-width: 81em;" href="<%= x.url %>"><%= x.title %></a><br>
<p style="color:gray;display: flex;flex-direction: column;width: 48em;margin-top: -8em;"><%= x.link %><br> <p style="color:gray;display: flex;flex-direction: column;width: 48em;margin-top: -8em;"><%= x.url %><br>
<% if (!isMobile) { %> <% if (!isMobile) { %>
<span style="color:white;width: 48em;display: flex;"><%= x.snippet %></span> <span class="search-result"><%- x.description %></span>
<% } %> <% } %>
<% if (isMobile) { %> <% if (isMobile) { %>
<span style="color:white;max-width: 15em;display: flex;"><%= x.snippet %></span> <span class="search-result"><%- x.description %></span>
<% } %> <% } %>
</p> </p>
</div> </div>
@ -465,12 +477,22 @@ Web
</a> </a>
</div> </div>
<script src="/css/custom-css.js"> </script>
<script src="/css/custom-css.js"> </script>
<script> if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(function(registrations) {
for (let registration of registrations) {
registration.unregister();
}
});
if ('caches' in window) {
caches.keys().then(function(cacheNames) {
cacheNames.forEach(function(cacheName) {
caches.delete(cacheName);
});
});
}
}</script>
</body> </body>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js');
}
</script>

View file

@ -1,7 +1,7 @@
<!-- <!--
This Source Code Form is subject to the terms of the GNU General Public License: This Source Code Form is subject to the terms of the GNU General Public License:
Copyright (C) 2021-2023 POKETUBE (https://codeberg.org/Ashley/poketube) Copyright (C) 2021-2024 POKETUBE (https://codeberg.org/Ashley/poketube)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -23,10 +23,10 @@
<link rel="manifest" href="/manifest.json"> <link rel="manifest" href="/manifest.json">
<link href=/css/yt-ukraine.svg?v=6 rel=icon> <link href=/css/yt-ukraine.svg?v=6 rel=icon>
<link href=/css/app-cdn.min.css rel=stylesheet> <link href=/css/app-cdn.min.css rel=stylesheet>
<link href=/css/app.main.css?v=25 rel=stylesheet> <link href=/css/app.main.css?v=333 rel=stylesheet>
<link href=/css/search.main.css rel=stylesheet> <link href=/css/search.main.css rel=stylesheet>
<link href=/css/watch.main.css rel=stylesheet> <link href=/css/watch.main.css rel=stylesheet>
<link rel="search" type="application/opensearchdescription+xml" title="PokeTube" href="https://poketube.fun/api/opensearch" /> <meta name="darkreader-lock"> <!-- tells dark reader that the site has a dark theme and to turn itself off -->
<meta content="Searching <%=q%> - Poke" property=og:title> <meta content="Searching <%=q%> - Poke" property=og:title>
<% if (q == "do the harlem shake") { %> <% if (q == "do the harlem shake") { %>
<meta content="DO THE HARLEM SHAKE" property=twitter:description> <meta content="DO THE HARLEM SHAKE" property=twitter:description>
@ -100,7 +100,19 @@ summary:hover{
font-family: ubuntu, sans-serif; font-family: ubuntu, sans-serif;
} }
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #333;
border: 1px solid #ccc;
border-radius: 5px;
text-align:center;
}
.container > * {
text-align:center !important;
}
.info > div { .info > div {
font-family:Ubuntu, sans-serif; font-family:Ubuntu, sans-serif;
} }
@ -276,6 +288,16 @@ border:solid;
transform: scale(1, 1); transform: scale(1, 1);
} }
.something-background-a {
background-image: url('<%- media_proxy_url %>/proxy?url=https://static.wikia.nocookie.net/omori/images/f/fc/Something_Bed_(2018_Demo).gif') !important;
}
.something-background-b {
background-image: url('<%- media_proxy_url %>/proxy?url=https://static.wikia.nocookie.net/omori/images/1/14/Hangman_%28Laptop%29.png') !important;
}
.something-background-c {
background-image: url('<%- media_proxy_url %>/proxy?url=https://static.wikia.nocookie.net/omori/images/6/67/Nanci_Before.png') !important;
}
.loading .spinner { .loading .spinner {
display: inline-block; display: inline-block;
border: 5px solid rgba(255, 255, 255, 0.2); border: 5px solid rgba(255, 255, 255, 0.2);
@ -332,11 +354,22 @@ video[counter].classList.add("shake");
</script> </script>
<% } %> <% } %>
<% if (q != "do the harlem shake") { %> <% if (q == "do a barrel roll") { %>
<style>
@keyframes barrelRoll {
to {
transform: rotate(360deg);
}
}
<% if (q != "want you gone") { %> <% if (q != "portal 2 ending") { %> <% if (q != "credits") { %> <% if (q != "glados") { %> body {
animation: barrelRoll 2s alternate; /* Adjust the duration as needed */
}
</style>
<% if (q != "something") { %> <% } %>
<% if (q != "do the harlem shake") { %> <% if (q != "want you gone") { %> <% if (q != "portal 2 ending") { %> <% if (q != "credits") { %> <% if (q != "glados") { %> <% if (q != "something") { %>
<a class="class" href="/143" style=font-family:Inter,sans-serif;color:#fff> <img style="transform: scale(1.3);width:8.5em;display: block;margin-left: auto;margin-right: auto;" src="/css/logo.svg?v=5"></a> <a class="class" href="/143" style=font-family:Inter,sans-serif;color:#fff> <img style="transform: scale(1.3);width:8.5em;display: block;margin-left: auto;margin-right: auto;" src="/css/logo.svg?v=5"></a>
@ -344,8 +377,8 @@ video[counter].classList.add("shake");
<% } %> <% } %>
<% } %> <% } %><% } %>
<% } %>
<% } %> <% } %>
<% } %> <% } %>
@ -376,7 +409,6 @@ video[counter].classList.add("shake");
<button class="btn btn-success" type=submit><i class="fa-light fa-search"></i></button> <button class="btn btn-success" type=submit><i class="fa-light fa-search"></i></button>
</form> </form>
<img src="https://search-metrics.poketube.fun/t/rep.gif" style="border:0;width: 0;visibility: hidden;">
</div> </div> </div> </div>
@ -468,6 +500,23 @@ Web </a>
</div> </div>
</div> </div>
<% if (q.includes("suicide")) { %>
<div class="container">
<h2 style="font-family: 'PokeTube Flex';font-size: large;text-align: left;font-stretch: ultra-expanded;
font-weight: 1000;">You are not alone</h2>
<p>
if you or somebody you know is having a bad time, talk to somebody today.
</p>
<p>
if you are from the US, call 988. if you arent <a href="https://www.psychologytoday.com/us/basics/suicide/suicide-prevention-hotlines-resources-worldwide"> see this url.</a>
<br> <br> dont worry, ur not alone <3 and you are really important btw!
</p>
</div>
<% } %>
<% if (!tab) { %> <% if (!tab) { %>
<% invresults.forEach (x => { %> <% invresults.forEach (x => { %>
@ -509,7 +558,7 @@ font-weight: 1000;
<div style="border-top: 1px solid var(--border-color);width: 100%;display: flex;gap: 43em;padding: 0;margin: 0;"> <div style="border-top: 1px solid var(--border-color);width: 100%;display: flex;gap: 43em;padding: 0;margin: 0;">
<% if (continuation !== "1") { %> <% if (Number(continuation) >= "2") { %>
<p style="text-align: left;margin-left: 16em;color: var(--text-secondary);text-decoration: none;"> <p style="text-align: left;margin-left: 16em;color: var(--text-secondary);text-decoration: none;">
<a href="/search?query=<%=q%>">First Page</a> </p> <a href="/search?query=<%=q%>">First Page</a> </p>
@ -518,7 +567,7 @@ font-weight: 1000;
</p> </p>
<% } %> <% } %>
<% if (continuation == "1" || !continuation) { %> <% if (Number(continuation) <= "0" || !continuation) { %>
<p style="text-align: left;margin-left: 16em;;color: var(--text-secondary);text-decoration: none;"> <p style="text-align: left;margin-left: 16em;;color: var(--text-secondary);text-decoration: none;">
<a href="/search?query=<%=q%>&continuation=2<% if (date) { %>&date=<%= date %><% } %><% if (duration) { %>&duration=<%= duration %><% } %><% if (sort) { %>&sort=<%= sort %><% } %> <a href="/search?query=<%=q%>&continuation=2<% if (date) { %>&date=<%= date %><% } %><% if (duration) { %>&duration=<%= duration %><% } %><% if (sort) { %>&sort=<%= sort %><% } %>
">Next Page</a> ">Next Page</a>
@ -575,11 +624,70 @@ font-weight: 1000;
</div> </div>
<script src="/css/custom-css.js"> </script> <script src="/css/custom-css.js"> </script>
<script>
// @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-3.0-or-later
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(function(registrations) {
for (let registration of registrations) {
registration.unregister();
}
});
if ('caches' in window) {
caches.keys().then(function(cacheNames) {
cacheNames.forEach(function(cacheName) {
caches.delete(cacheName);
});
});
}
}
document.addEventListener("DOMContentLoaded", function() {
if (localStorage.getItem('liar.')) {
var thumbnails = document.querySelectorAll('.thumbnail');
function changeFavicon(newFaviconUrl) {
var oldFavicon = document.querySelector('link[rel="icon"]');
if (oldFavicon) {
oldFavicon.remove();
}
var link = document.createElement('link');
link.rel = 'icon';
link.href = newFaviconUrl;
document.head.appendChild(link);
}
changeFavicon("<%- media_proxy_url %>/proxy?url=https://static.wikia.nocookie.net/omori/images/d/d8/Something_Float_%282018_Demo%29.gif")
var textList = [
"sunny... i love you",
"SUNNY... I'm... sorry..."
];
// Select four different text elements
var textElements = document.querySelectorAll('.title');
textElements.forEach(function(element, index) {
element.textContent = textList[index % textList.length]; // Use modulo to cycle through textList
});
var backgroundClasses = ['something-background-a', 'something-background-b', 'something-background-c'];
// Loop through each thumbnail element and assign a random background image class
thumbnails.forEach(function(thumbnail) {
var randomIndex = Math.floor(Math.random() * backgroundClasses.length);
thumbnail.classList.add(backgroundClasses[randomIndex]);
});
document.title = "sunny..im..sorry"
var audio = new Audio('https://p.poketube.fun/https://cdn.glitch.global/d68d17bb-f2c0-4bc3-993f-50902734f652/Lost_at_Sea.ogg?v=1706879048533');
audio.loop = true;
audio.autoplay = true;
}
});
// @license-end
</script>
</body> </body>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js');
}
</script>

431
html/translate.ejs Normal file
View file

@ -0,0 +1,431 @@
<!--
This Source Code Form is subject to the terms of the GNU General Public License:
Copyright (C) 2021-2024 PokeTube (https://codeberg.org/Ashley/poketube)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see https://www.gnu.org/licenses/.
-->
<!doctype html>
<html lang="en">
<head>
<title>PokeTranslate</title>
<link rel="icon" href="/static/yt-ukraine.svg">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta content="PokeTranslate" property=og:title>
<meta content="Translate text - Anonymously!" property=twitter:description>
<meta content="https://cdn.glitch.global/d68d17bb-f2c0-4bc3-993f-50902734f652/aa70111e-5bcd-4379-8b23-332a33012b78.image.png?v=1701898829884" property="og:image" />
<meta content=summary_large_image name=twitter:card>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'">
<meta name="referrer" content="no-referrer">
<link rel="manifest" href="/manifest.json">
<style>
.center {
text-align: center;
}
.wrap {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.wrap.languages {
flex-wrap: nowrap;
margin-bottom: 20px;
}
#could_not_switch_languages_text {
color: red;
}
.item {
width: 100%;
height: 150px;
border-radius:1em;
}
.item-wrapper {
display: flex;
flex-wrap: wrap;
justify-content: center;
width: 450px;
margin: 5px 10px;
}
.language,
.switch_languages {
display: flex;
}
.language {
margin: 0px 10px;
}
.switch_languages {
margin: 0px 5px;
}
#switchbutton {
white-space: nowrap;
}
button {
font-size: 1rem;
padding: 4px 10px;
border: 2px solid #888888;
}
input,
select,
textarea {
width: 100%;
font-size: 1rem;
padding: 4px;
border: 2px solid #888888;
}
textarea {
resize: vertical;
height: 5rem;
font-family: sans-serif;
width: 100%;
}
input:focus,
select:focus,
textarea:focus,
button:focus {
border-color: #478061;
outline: 1px solid #478061;
}
body {
justify-content: center;
font-family: sans-serif;
}
#definitions_and_translations {
display: grid;
margin: auto;
width: 1100px;
gap: 10px;
grid-template-areas: "definitions translations";
}
.def_type {
color: #007979;
text-transform: capitalize;
}
.syn {
color: #804700;
}
.syn_type {
color: #007979;
}
.use_in_sentence {
color: #009902;
}
.definitions li:not(:last-child) {
margin-bottom: 1rem;
}
@media screen and (max-width: 1200px) {
#definitions_and_translations {
display: grid;
width: 90vw;
grid-template-areas:
"definitions definitions"
"translations translations";
}
}
div.definitions {
grid-area: definitions;
}
div.translations {
grid-area: translations;
}
body {
background-color: #212529;
color: #f8f9fa;
}
#could_not_switch_languages_text {
color: #F13333;
}
a:visited {
color: #9759f6;
text-decoration: none;
}
a {
color: #599bf6;
text-decoration: none;
}
input,
select,
button,
textarea {
background-color: #131618;
border-color: #495057;
color: #f8f9fa;
}
.def_type {
color: cyan;
text-transform: capitalize;
}
.syn {
color: burlywood;
}
.syn_type {
color: cyan;
}
.use_in_sentence {
color: yellow;
}
</style>
<% if (isMobile) { %>
<style>
body {
overflow: auto;
}
</style>
<% } %>
<% if (!isMobile) { %>
<style>
body {
overflow: hidden;
}
</style>
<% } %>
</head>
<body>
<div style="border-radius: 3em;background: #1a1a1a;padding: 1em;display: flex;flex-direction: column;height: fit-content;align-self: center;margin-top: 6em;">
<header class="center"><h1>PokeTranslate</h1></header>
<form action="/translate" method="GET" id="translation-form">
<!-- from and to language -->
<div class="wrap languages">
<div class="language">
<% const languageOptions = [
{ code: 'autodetect', name: 'Autodetect' },
{ code: 'af', name: 'Afrikaans' },
{ code: 'sq', name: 'Albanian' },
{ code: 'am', name: 'Amharic' },
{ code: 'ar', name: 'Arabic' },
{ code: 'hy', name: 'Armenian' },
{ code: 'as', name: 'Assamese' },
{ code: 'ay', name: 'Aymara' },
{ code: 'az', name: 'Azerbaijani' },
{ code: 'bm', name: 'Bambara' },
{ code: 'eu', name: 'Basque' },
{ code: 'be', name: 'Belarusian' },
{ code: 'bn', name: 'Bengali' },
{ code: 'bh', name: 'Bhojpuri' },
{ code: 'bs', name: 'Bosnian' },
{ code: 'bg', name: 'Bulgarian' },
{ code: 'ca', name: 'Catalan' },
{ code: 'ceb', name: 'Cebuano' },
{ code: 'ny', name: 'Chichewa' },
{ code: 'zh-cn', name: 'Chinese (Simplified)' },
{ code: 'zh-tw', name: 'Chinese (Traditional)' },
{ code: 'co', name: 'Corsican' },
{ code: 'hr', name: 'Croatian' },
{ code: 'cs', name: 'Czech' },
{ code: 'da', name: 'Danish' },
{ code: 'dv', name: 'Dhivehi' },
{ code: 'doi', name: 'Dogri' },
{ code: 'nl', name: 'Dutch' },
{ code: 'en', name: 'English' },
{ code: 'eo', name: 'Esperanto' },
{ code: 'et', name: 'Estonian' },
{ code: 'ee', name: 'Ewe' },
{ code: 'tl', name: 'Filipino' },
{ code: 'fi', name: 'Finnish' },
{ code: 'fr', name: 'French' },
{ code: 'fy', name: 'Frisian' },
{ code: 'gl', name: 'Galician' },
{ code: 'ka', name: 'Georgian' },
{ code: 'de', name: 'German' },
{ code: 'el', name: 'Greek' },
{ code: 'gn', name: 'Guarani' },
{ code: 'gu', name: 'Gujarati' },
{ code: 'ht', name: 'Haitian Creole' },
{ code: 'ha', name: 'Hausa' },
{ code: 'haw', name: 'Hawaiian' },
{ code: 'he', name: 'Hebrew' },
{ code: 'hi', name: 'Hindi' },
{ code: 'hmn', name: 'Hmong' },
{ code: 'hu', name: 'Hungarian' },
{ code: 'is', name: 'Icelandic' },
{ code: 'ig', name: 'Igbo' },
{ code: 'ilo', name: 'Ilocano' },
{ code: 'id', name: 'Indonesian' },
{ code: 'ga', name: 'Irish' },
{ code: 'it', name: 'Italian' },
{ code: 'ja', name: 'Japanese' },
{ code: 'jv', name: 'Javanese' },
{ code: 'kn', name: 'Kannada' },
{ code: 'kk', name: 'Kazakh' },
{ code: 'km', name: 'Khmer' },
{ code: 'rw', name: 'Kinyarwanda' },
{ code: 'kok', name: 'Konkani' },
{ code: 'ko', name: 'Korean' },
{ code: 'kri', name: 'Krio' },
{ code: 'ku', name: 'Kurdish (Kurmanji)' },
{ code: 'sd', name: 'Sindhi' },
{ code: 'si', name: 'Sinhala' },
{ code: 'sk', name: 'Slovak' },
{ code: 'sl', name: 'Slovenian' },
{ code: 'so', name: 'Somali' },
{ code: 'es', name: 'Spanish' },
{ code: 'su', name: 'Sundanese' },
{ code: 'sw', name: 'Swahili' },
{ code: 'sv', name: 'Swedish' },
{ code: 'tg', name: 'Tajik' },
{ code: 'ta', name: 'Tamil' },
{ code: 'tt', name: 'Tatar' },
{ code: 'te', name: 'Telugu' },
{ code: 'th', name: 'Thai' },
{ code: 'ti', name: 'Tigrinya' },
{ code: 'ts', name: 'Tsonga' },
{ code: 'tr', name: 'Turkish' },
{ code: 'tk', name: 'Turkmen' },
{ code: 'twi', name: 'Twi' },
{ code: 'uk', name: 'Ukrainian' },
{ code: 'ur', name: 'Urdu' },
{ code: 'ug', name: 'Uyghur' },
{ code: 'uz', name: 'Uzbek' },
{ code: 'vi', name: 'Vietnamese' },
{ code: 'cy', name: 'Welsh' },
{ code: 'xh', name: 'Xhosa' },
{ code: 'yi', name: 'Yiddish' },
{ code: 'yo', name: 'Yoruba' },
{ code: 'zu', name: 'Zulu' }
]; %>
<!-- Source language select -->
<select name="from_language" id="from_language" style="margin-right: 1em;border-radius: 1em;padding: 7px;" aria-label="Source language">
<% languageOptions.forEach(language => { %>
<option value="<%= language.code %>" <%= language.code === (from_language || 'autodetect') ? 'selected' : '' %>><%= language.name %></option>
<% }); %>
</select>
<!-- Target language select -->
<select name="to_language" id="to_language" style="margin-right: 1em;border-radius: 1em;padding: 7px;" aria-label="Target language">
<% languageOptions.slice(1).forEach(language => { %>
<option value="<%= language.code %>" <%= language.code === to_language ? 'selected' : '' %>><%= language.name %></option>
<% }); %>
</select>
</div>
</div>
<!-- text boxes -->
<div class="wrap">
<div class="item-wrapper">
<textarea autofocus class="item" id="input" name="input" dir="auto" placeholder="<%- text %>"><%- text %>
</textarea>
</div>
<div class="item-wrapper">
<textarea id="output" class="translation item" dir="auto" placeholder="Translation" readonly> <%- translation %> </textarea>
</div>
</div>
<br>
<div class="center">
<!-- translate button -->
<button type="submit" style="border-radius: 1em;padding: 7px;">Translate :3</button>
</div>
<br>
<br>
</div>
</form>
</div>
<br>
<br>
<br>
<script>
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
// this code submits the translation form when pressing Ctrl/Meta+Enter while focussed on the input text field
document.getElementById("input").addEventListener("keydown", function(event) {
if (event.keyCode === 13 && (event.metaKey || event.ctrlKey)) {
document.getElementById("translation-form").submit();
}
});
// Auto resize textarea to fit words inside it without need to scroll -- Thanks to: https://stackoverflow.com/a/25621277
var input = document.getElementById("input");
var output = document.getElementById("output");
input.setAttribute("style", "height:" + output.scrollHeight + "px;overflow-y:scroll;");
output.setAttribute("style", "height:" + output.scrollHeight + "px;overflow-y:scroll;");
input.addEventListener("input", function(e) {
this.style.height = 150 + "px";
this.style.height = this.scrollHeight + "px";
});
// @license-end
</script>
<script src="/static/custom-css.js"></script>
</body>
</html>

View file

@ -43,6 +43,20 @@
"branch": "dev" "branch": "dev"
} }
} }
],
[
"poke.ashley0143.xyz",
{
"uri": "https://poke.ashley0143.xyz",
"CLOUDFLARE": true,
"piwik": false,
"region": "🇺🇸",
"software": {
"name": "poketube",
"version": "latest",
"branch": "dev"
}
}
] ]
] ]

View file

@ -48,7 +48,7 @@ app.use(function (req, res, next) {
app.use(function (_req, res, next) { app.use(function (_req, res, next) {
res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Origin", "*");
res.setHeader("Cache-Control", "public, max-age=232337763"); // cache header res.setHeader("Cache-Control", "public, max-age=864000"); // cache header
res.setHeader("poketube-cacher", "PROXY_FILES"); res.setHeader("poketube-cacher", "PROXY_FILES");
next(); next();
@ -60,7 +60,7 @@ app.use(function (_req, res, next) {
*/ */
const proxy = async (req, res) => { const proxy = async (req, res) => {
const { fetch } = await import("undici") const { fetch } = await import("undici")
res.setHeader("Cache-Control", "public, max-age=232337763"); // cache header res.setHeader("Cache-Control", "public, max-age=864000"); // cache header
try { try {
let url; let url;
@ -98,9 +98,9 @@ const listener = (req, res) => {
app.get("/", (req, res) => { app.get("/", (req, res) => {
var json = { var json = {
status: "200", status: "200",
version: "1.2.0", version: "1.3.0",
URL_WHITELIST, URL_WHITELIST,
cache: "max-age-232337763", cache: "max-age-864000",
}; };
res.json(json); res.json(json);

View file

@ -1,7 +1,7 @@
{ {
"name": "poketube", "name": "poke",
"version": "20.23", "version": "20.24",
"description": "Libre youtube front-end", "description": "Libre youtube and google front-end",
"main": "server.js", "main": "server.js",
"scripts": { "scripts": {
"start": "node server.js" "start": "node server.js"
@ -32,7 +32,8 @@
"express-rate-limit": "^7.0.2", "express-rate-limit": "^7.0.2",
"toobusy-js": "^0.5.1", "toobusy-js": "^0.5.1",
"quick.db": "^7.1.3", "quick.db": "^7.1.3",
"google-it": "^1.6.4" "activitypub-express": "^4.4.1",
"duck-duck-scrape": "^2.2.5"
}, },
"engines": { "engines": {
"node": "16.x" "node": "16.x"
@ -43,7 +44,7 @@
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"keywords": [ "keywords": [
"poketube", "poketube",
"privite", "private",
"ytdl" "ytdl"
] ]
} }

127
poke-cli.sh Normal file
View file

@ -0,0 +1,127 @@
#!/usr/bin/env bash
#
# Copyright (C) 2024-20xx Poke! (https://codeberg.org/ashley/poke)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
function display_help {
echo "Usage: $0 <search_query>"
echo " --help you are here lol"
echo " --version version information."
echo " --license license stuff"
}
function display_version {
echo "poke-cli version 1.2
Play videos from your terminal!
https://codeberg.org/ashley/poke
Copyright (C) 2024-202x Poke
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
"
}
# Display license information
function display_license {
cat <<EOF
Poke-CLI for GNU/Linux systems
Copyright (C) Poke 2024-20xx
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
EOF
}
case $1 in
--help)
display_help
exit 0
;;
--version)
display_version
exit 0
;;
--license)
display_license
exit 0
;;
esac
if [ $# -eq 0 ]; then
echo "Usage: $0 <search_query> / see --help for more info :D"
exit 1
fi
# config
poke_instance="https://poketube.fun"
invid_api_url="https://invid-api.poketube.fun"
search_query=$1
player="mpv"
if ! command -v jq &> /dev/null && ! command -v gojq &> /dev/null; then
echo "Error: jq or gojq not found. Please install them to run the script."
exit 1
fi
json_data=$(curl -s "$invid_api_url/api/v1/search?q=${search_query// /+}&type=video")
video_count=$(echo "$json_data" | jq -r '. | length')
if [ $video_count -eq 0 ]; then
echo "Nyo videos found for the given search query ;_;"
exit 1
fi
echo "Select a vid to play:"
echo
for i in $(seq 0 $(($video_count - 1))); do
title=$(echo "$json_data" | jq -r ".[$i].title")
author=$(echo "$json_data" | jq -r ".[$i].author")
echo "[$(($i + 1))] $title by $author"
done
read -p "Enter the thingy umm number of the video to play (1-$video_count): " selection
if ! [[ "$selection" =~ ^[1-9][0-9]*$ ]] || [ "$selection" -lt 1 ] || [ "$selection" -gt "$video_count" ]; then
echo "enter a number between 1 and $video_count lol"
exit 1
fi
video_url=$(echo "$json_data" | jq -r ".[$(($selection - 1))].videoId")
echo "Starting $player..."
echo "please wait - this may take some time lol..."
$player "$poke_instance/watch?v=$video_url"

View file

@ -16,21 +16,36 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see https://www.gnu.org/licenses/. along with this program. If not, see https://www.gnu.org/licenses/.
--> -->
<!DOCTYPE html><html> <!DOCTYPE html>
<head> <html><head>
<title>PokeTube</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head> <title>Poke - No interenet!1!1!1!111!</title>
</head>
<body> <body>
<div id="error-page"> <div id="error-page">
<div id="error-page-content" align="center"> <div id="error-page-content" align="center">
<h1 style="color:#fff;font-family:'PokeTube Flex',sans-serif;font-weight:900;white-space:yes;font-style: italic;font-size: 45px;" align="center">Oh no ;-;</h1> <h1 style="color:#fff;font-family:'PokeTube Flex',sans-serif;font-weight:900;white-space:yes;font-style: italic;font-size: 45px;" align="center">Oh nyo &gt;~&lt;</h1>
<h3>Looks like you're offline, check your internet connection and try again.</h3> <h3 style="font-family: sans-serif;">Connectivity to the server has been lost qwq <br> Seems like you're offline - u check your internet connection and try again</h3><br>
<p>or u can just view poke!'s <a href="https://status.poketube.fun">status page</a> yk~ </p>
</div> </div>
</div> </div>
</body>
</html>
<style> <style>
.downnav {
position: fixed;
padding:10px;
bottom: 0;
left: 0;
width: 100%;
margin: auto;
overflow: hidden;
z-index: 1;
color:#fff;
background-color: #0f0f0f;
}
body{ body{
background:#111; background:#111;
margin: auto; margin: auto;
@ -38,10 +53,11 @@
} }
p,a,h3{ p,a,h3{
text-align:center; text-align:center;
font-family: sans-serif;
color:#fff; color:#fff;
} }
nav,error-page,div{ nav,error-page,div{
justify-content: center; justify-content: center;
background: #111 background: #111
} }
</style> </style></body></html>

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-3.0-or-later
'use strict'; 'use strict';
var cacheVersion = 1; var cacheVersion = 1;
@ -32,3 +34,5 @@ this.addEventListener('fetch', event => {
); );
} }
}); });
// @license-end

View file

@ -2,7 +2,7 @@
PokeTube is an Free/Libre youtube front-end. this is our main file. PokeTube is an Free/Libre youtube front-end. this is our main file.
Copyright (C) 2021-2023 POKETUBE (https://codeberg.org/Ashley/poketube) Copyright (C) 2021-2024 POKETUBE (https://codeberg.org/Ashley/poketube)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -35,7 +35,7 @@
const u = await media_proxy(); const u = await media_proxy();
initlog("Loading..."); initlog("Loading...");
initlog( initlog(
"[Welcome] Welcome To PokeTube :3 " + "[Welcome] Welcome To Poke - The ultimate privacy app - :3 " +
"Running " + "Running " +
`Node ${process.version} - V8 v${ `Node ${process.version} - V8 v${
process.versions.v8 process.versions.v8
@ -65,8 +65,8 @@
const limiter = rateLimit({ const limiter = rateLimit({
windowMs:45 * 1000, // 30 Seconds windowMs:45 * 1000, // 45 Seconds
max: 886, // limit each IP to 870 requests per windowMs max: 886, // limit each IP to 866 requests per windowMs
}); });
var app = modules.express(); var app = modules.express();
@ -101,6 +101,7 @@ toobusy.maxLag(3500);
}); });
toobusy.onLag(function(currentLag) { toobusy.onLag(function(currentLag) {
process.exit(1);
console.log("Event loop lag detected! Latency: " + currentLag + "ms"); console.log("Event loop lag detected! Latency: " + currentLag + "ms");
}); });
@ -136,6 +137,9 @@ toobusy.maxLag(3500);
} }
res.header("secure-poketube-instance", "1"); res.header("secure-poketube-instance", "1");
// opt out of googles "FLOC" bullcrap :p See https://spreadprivacy.com/block-floc-with-duckduckgo/
res.header("Permissions-Policy", "interest-cohort=()")
res.header("software-name", "poke")
next(); next();
}); });
@ -157,8 +161,9 @@ toobusy.maxLag(3500);
app.use(function (req, res, next) { app.use(function (req, res, next) {
res.header("X-PokeTube-Youtube-Client-Name", "1"); res.header("X-PokeTube-Youtube-Client-Name", "1");
res.header("X-PokeTube-Youtube-Client-Version", "2.20210721.00.00"); res.header("Hey-there", "Do u wanna help poke? contributons are welcome :3 https://codeberg.org/Ashley/poke")
res.header("X-PokeTube-Speeder", "6 seconds no cache, 780ms w/cache"); res.header("X-PokeTube-Youtube-Client-Version", "2.20240111.00.00");
res.header("X-PokeTube-Speeder", "3 seconds no cache, 280ms w/cache");
if (req.url.match(/^\/(css|js|img|font)\/.+/)) { if (req.url.match(/^\/(css|js|img|font)\/.+/)) {
res.setHeader( res.setHeader(
"Cache-Control", "Cache-Control",

View file

@ -130,8 +130,13 @@ module.exports = function (app, config, renderTemplate) {
if (req.params.v && /[a-zA-Z0-9]+/.test(req.params.v)) { if (req.params.v && /[a-zA-Z0-9]+/.test(req.params.v)) {
const isvld = await core.isvalidvideo(req.params.v); const isvld = await core.isvalidvideo(req.params.v);
if (isvld) { if (isvld && req.params.v.length >= 10) {
return res.redirect(`/watch?v=${req.params.v}`); return res.redirect(`/watch?v=${req.params.v}`);
} else {
return renderTemplate(res, req, "404.ejs", {
isOldWindows,
random
});
} }
} }

View file

@ -29,12 +29,12 @@ function getJson(str) {
const pkg = require("../../../package.json"); const pkg = require("../../../package.json");
const cnf = require("../../../config.json"); const cnf = require("../../../config.json");
const verfull = "v23.1311-JeSsIcA-MAJOR-stable-dev-nonLTS-git-MTcwMDI5ODc4OQ=="; const verfull = "v24.2801-JeSsIcA-MAJOR-stable-dev-nonLTS-git-MTcwNjQzMTc0OQ==";
const versmol = "v23.1311-JeSsIcA" const versmol = "v24.2801-JeSsIcA"
const branch = "dev/master"; const branch = "dev/master";
const codename = "jessica"; const codename = "jessica";
const versionnumber = "272"; const versionnumber = "273";
const relaseunixdate = "MTcwMDI5ODc4OQ==" const relaseunixdate = "MTcwNjQzMTc0OQ=="
const updatequote = "Empty your cup so that it may be filled; become devoid to gain totality. - Bruce Lee" const updatequote = "Empty your cup so that it may be filled; become devoid to gain totality. - Bruce Lee"
@ -52,7 +52,7 @@ module.exports = function (app, config, renderTemplate) {
}); });
app.get("/vi/:v/:t", async function (req, res) { app.get("/vi/:v/:t", async function (req, res) {
var url = `https://invidious.snopyta.org/vi/${req.params.v}/${req.params.t}` var url = `https://vid.puffyan.us/vi/${req.params.v}/${req.params.t}`
let f = await modules.fetch(url + `?cachefixer=${btoa(Date.now())}`, { let f = await modules.fetch(url + `?cachefixer=${btoa(Date.now())}`, {
method: req.method, method: req.method,
@ -63,7 +63,7 @@ module.exports = function (app, config, renderTemplate) {
}); });
app.get("/avatars/:v", async function (req, res) { app.get("/avatars/:v", async function (req, res) {
var url = `https://invidious.snopyta.org/ggpht/${req.params.v}`; var url = `https://vid.puffyan.us/ggpht/${req.params.v}`;
let f = await modules.fetch(url + `?cachefixer=${btoa(Date.now())}`, { let f = await modules.fetch(url + `?cachefixer=${btoa(Date.now())}`, {
method: req.method, method: req.method,
@ -73,7 +73,7 @@ app.get("/avatars/:v", async function (req, res) {
}); });
app.get("/ggpht/:v", async function (req, res) { app.get("/ggpht/:v", async function (req, res) {
var url = `https://invidious.snopyta.org/ggpht/${req.params.v}`; var url = `https://vid.puffyan.us/ggpht/${req.params.v}`;
let f = await modules.fetch(url + `?cachefixer=${btoa(Date.now())}`, { let f = await modules.fetch(url + `?cachefixer=${btoa(Date.now())}`, {
method: req.method, method: req.method,
@ -84,7 +84,7 @@ app.get("/avatars/:v", async function (req, res) {
app.get("/avatars/ytc/:v", async function (req, res) { app.get("/avatars/ytc/:v", async function (req, res) {
var url = `https://invidious.snopyta.org/ggpht/ytc/${req.params.v.replace("ytc", "")}`; var url = `https://vid.puffyan.us/ggpht/ytc/${req.params.v.replace("ytc", "")}`;
let f = await modules.fetch(url + `?cachefixer=${btoa(Date.now())}`, { let f = await modules.fetch(url + `?cachefixer=${btoa(Date.now())}`, {
method: req.method, method: req.method,
@ -114,7 +114,7 @@ app.get("/avatars/:v", async function (req, res) {
var format = "mp3"; var format = "mp3";
} }
const url = `https://tube-proxy.poketube.fun/proxy/media/${v}/${q}`; const url = `https://tube.kuylar.dev/proxy/media/${v}/${q}`;
res.redirect(url); res.redirect(url);
}); });

View file

@ -50,9 +50,10 @@ const ChannelTabs = {
shorts: "c2hvcnRz", shorts: "c2hvcnRz",
videos: "dmlkZW9z", videos: "dmlkZW9z",
streams: "c3RyZWFtcw==", // or "live" streams: "c3RyZWFtcw==", // or "live"
channels:"Y2hhbm5lbHM=", channels: "Y2hhbm5lbHM=",
store:"c3RvcmU=", store: "c3RvcmU=",
released:"cmVsZWFzZWQ=" released: "cmVsZWFzZWQ=",
playlist: "cGxheWxpc3Rz",
}; };
module.exports = function (app, config, renderTemplate) { module.exports = function (app, config, renderTemplate) {
@ -84,14 +85,16 @@ module.exports = function (app, config, renderTemplate) {
res.json(ChannelTabs); res.json(ChannelTabs);
}); });
app.get("/search", async (req, res) => { app.get("/search", async (req, res) => {
const query = req.query.query; const query = req.query.query;
const tab = req.query.tab; const tab = req.query.tab;
const { fetch } = await import("undici"); const { fetch } = await import("undici");
const search = require("google-it"); var media_proxy = config.media_proxy;
if (req.useragent.source.includes("Pardus")) {
var media_proxy = "https://media-proxy.ashley0143.xyz";
}
var uaos = req.useragent.os; var uaos = req.useragent.os;
var IsOldWindows; var IsOldWindows;
@ -132,9 +135,13 @@ module.exports = function (app, config, renderTemplate) {
try { try {
const headers = {}; const headers = {};
const xmlData = await fetch(`https://invid-api.poketube.fun/api/v1/search?q=${encodeURIComponent( const xmlData = await fetch(
`https://invid-api.poketube.fun/api/v1/search?q=${encodeURIComponent(
query query
)}&page=${encodeURIComponent(continuation)}&date=${date}&type=${type}&duration=${duration}&sort=${sort}&hl=en+gb`) )}&page=${encodeURIComponent(
continuation
)}&date=${date}&type=${type}&duration=${duration}&sort=${sort}&hl=en+gb`
)
.then((res) => res.text()) .then((res) => res.text())
.then((txt) => getJson(txt)); .then((txt) => getJson(txt));
@ -148,7 +155,7 @@ module.exports = function (app, config, renderTemplate) {
IsOldWindows, IsOldWindows,
tab, tab,
continuation, continuation,
media_proxy_url: config.media_proxy, media_proxy_url: media_proxy,
results: "", results: "",
q: query, q: query,
summary: "", summary: "",
@ -159,12 +166,24 @@ module.exports = function (app, config, renderTemplate) {
} }
}); });
app.get("/im-feeling-lucky", function (req, res) {
res.send("WIP");
});
app.get("/web", async (req, res) => { app.get("/web", async (req, res) => {
const query = req.query.query; const query = req.query.query;
const tab = req.query.tab; const tab = req.query.tab;
const search = require("google-it"); const { fetch } = await import("undici");
const search = await fetch(
`https://4get.sudovanilla.com/api/v1/web?s=${query}`
);
const web = getJson(await search.text());
if (req.query.lucky === "true") {
res.redirect("/im-feeling-lucky?query=" + query);
}
var uaos = req.useragent.os; var uaos = req.useragent.os;
var IsOldWindows; var IsOldWindows;
@ -199,7 +218,8 @@ module.exports = function (app, config, renderTemplate) {
let continuation = req.query.continuation || ""; let continuation = req.query.continuation || "";
try { try {
search({ query: `${req.query.query}` }).then((results) => { const results = web.web;
renderTemplate(res, req, "search-web.ejs", { renderTemplate(res, req, "search-web.ejs", {
j: "", j: "",
IsOldWindows, IsOldWindows,
@ -211,7 +231,6 @@ module.exports = function (app, config, renderTemplate) {
q: query, q: query,
summary: "", summary: "",
}); });
});
} catch (error) { } catch (error) {
console.error(`Error while searching for '${query}':`, error); console.error(`Error while searching for '${query}':`, error);
res.redirect("/"); res.redirect("/");
@ -221,7 +240,21 @@ module.exports = function (app, config, renderTemplate) {
app.get("/channel/", async (req, res) => { app.get("/channel/", async (req, res) => {
const { fetch } = await import("undici"); const { fetch } = await import("undici");
try { try {
const ID = req.query.id; var media_proxy = config.media_proxy;
if (req.useragent.source.includes("Pardus")) {
var media_proxy = "https://media-proxy.ashley0143.xyz";
}
var ID = req.query.id;
if (ID.endsWith("@youtube.com")) {
ID = ID.slice(0, -"@youtube.com".length);
}
if (ID.endsWith("@poketube.fun")) {
ID = ID.slice(0, -"@poketube.fun".length);
}
const tab = req.query.tab; const tab = req.query.tab;
const cache = {}; const cache = {};
@ -268,17 +301,33 @@ module.exports = function (app, config, renderTemplate) {
const communityUrl = `${apiUrl}${atob( const communityUrl = `${apiUrl}${atob(
ChannelTabs.community ChannelTabs.community
)}/${ID}/?hl=en-US`; )}/${ID}/?hl=en-US`;
const PlaylistUrl = `${apiUrl}${atob(
ChannelTabs.playlist
)}/${ID}/?hl=en-US`;
const channelINVUrl = `${apiUrl}${ID}/`; const channelINVUrl = `${apiUrl}${ID}/`;
var [tj, shorts, stream, c, cinv] = await Promise.all([ var [tj, shorts, playlist, stream, c, cinv] = await Promise.all([
getChannelData(channelUrl), getChannelData(channelUrl),
getChannelData(shortsUrl), getChannelData(shortsUrl),
getChannelData(PlaylistUrl),
getChannelData(streamUrl), getChannelData(streamUrl),
getChannelData(communityUrl), getChannelData(communityUrl),
getChannelData(channelINVUrl), getChannelData(channelINVUrl),
]); ]);
function getThumbnailUrl(video) {
const maxresDefaultThumbnail = video.videoThumbnails.find(
(thumbnail) => thumbnail.quality === "maxresdefault"
);
if (maxresDefaultThumbnail) {
return `https://vid.puffyan.us/vi/${video.videoId}/maxresdefault.jpg`;
} else {
return `https://vid.puffyan.us/vi/${video.videoId}/hqdefault.jpg`;
}
}
cache[ID] = { cache[ID] = {
result: { result: {
tj, tj,
@ -315,13 +364,15 @@ module.exports = function (app, config, renderTemplate) {
cinv, cinv,
convert, convert,
turntomins, turntomins,
media_proxy_url: config.media_proxy, media_proxy_url: media_proxy,
dnoreplace, dnoreplace,
getThumbnailUrl,
continuation, continuation,
wiki: "", wiki: "",
getFirstLine, getFirstLine,
isMobile: req.useragent.isMobile, isMobile: req.useragent.isMobile,
about, about,
playlist,
subs: subs:
typeof subscribers === "string" typeof subscribers === "string"
? subscribers.replace("subscribers", "") ? subscribers.replace("subscribers", "")

View file

@ -27,6 +27,13 @@ const sha384 = modules.hash;
const notice = const notice =
"/* the code is Licensed in gpl-3.0-or-later. This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. See the GNU General Public License for more detailsYou should have received a copy of the GNU General Public Licensealong with this program. If not, see <https://www.gnu.org/licenses/>. - add the param nomin to view source code. (eg poketube.fun/css/poketube.css?nomin=true) */"; "/* the code is Licensed in gpl-3.0-or-later. This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. See the GNU General Public License for more detailsYou should have received a copy of the GNU General Public Licensealong with this program. If not, see <https://www.gnu.org/licenses/>. - add the param nomin to view source code. (eg poketube.fun/css/poketube.css?nomin=true) */";
function getJson(str) {
try {
return JSON.parse(str);
} catch {
return null;
}
}
module.exports = function (app, config, renderTemplate) { module.exports = function (app, config, renderTemplate) {
var html_location = "./css/"; var html_location = "./css/";
var location_pwa = "./pwa/"; var location_pwa = "./pwa/";
@ -67,10 +74,61 @@ module.exports = function (app, config, renderTemplate) {
} }
}); });
app.get("/rewind", function (req, res) {
renderTemplate(res, req, "rewind.ejs");
});
app.get("/translate", async function (req, res) {
const { fetch } = await import("undici");
const api_url = "https://simplytranslate.org/api/translate";
// Fetch translation data
const translationResponse = await fetch(
`${api_url}?from=${req.query.from_language}&to=${req.query.to_language}&text=${req.query.input}&engine=google`
);
// Check if the request was successful (status code 200)
const translationData = await translationResponse.json();
// Extract translated_text from the response
const translatedText = translationData.translated_text;
// Render the template with the translated text
renderTemplate(res, req, "translate.ejs", {
translation: translatedText,
text: req.query.input || "enter text here",
from_language: req.query.from_language,
to_language: req.query.to_language,
isMobile: req.useragent.isMobile,
});
});
app.get("/domains", function (req, res) { app.get("/domains", function (req, res) {
renderTemplate(res, req, "domains.ejs"); renderTemplate(res, req, "domains.ejs");
}); });
app.get("/apps", function (req, res) {
renderTemplate(res, req, "apps.ejs");
});
app.get("/playlist", async function (req, res) {
const { fetch } = await import("undici");
if (!req.query.list) res.redirect("/");
if (req.useragent.isMobile) res.redirect("/");
const playlist = await fetch(
`https://invid-api.poketube.fun/api/v1/playlists/${req.query.list}?hl=en-us`
);
const p = getJson(await playlist.text());
var mediaproxy = config.media_proxy;
renderTemplate(res, req, "playlist.ejs", {
p,
mediaproxy,
});
});
app.get("/license", function (req, res) { app.get("/license", function (req, res) {
renderTemplate(res, req, "license.ejs"); renderTemplate(res, req, "license.ejs");
}); });
@ -95,11 +153,6 @@ module.exports = function (app, config, renderTemplate) {
res.sendFile("manifest.json", { root: location_pwa }); res.sendFile("manifest.json", { root: location_pwa });
}); });
app.get("/service-worker.js", function (req, res) {
res.sendFile("service-worker.js", { root: location_pwa });
});
app.get("/customize", function (req, res) { app.get("/customize", function (req, res) {
const tab = req.query.tab; const tab = req.query.tab;
@ -110,6 +163,10 @@ module.exports = function (app, config, renderTemplate) {
const cssDir = "./css/"; const cssDir = "./css/";
app.get("/favicon.ico", function (req, res) {
res.sendFile("favicon.ico", { root: cssDir });
});
app.get("/css/:id", (req, res) => { app.get("/css/:id", (req, res) => {
const filePath = path.join(cssDir, req.params.id); const filePath = path.join(cssDir, req.params.id);
if (!fs.existsSync(filePath)) { if (!fs.existsSync(filePath)) {
@ -123,7 +180,11 @@ module.exports = function (app, config, renderTemplate) {
const minimizedCss = new CleanCSS().minify(css).styles; const minimizedCss = new CleanCSS().minify(css).styles;
// Serve the minimized CSS file // Serve the minimized CSS file
res.header("Content-Type", "text/css"); res.header("Content-Type", "text/css");
res.send(notice + " " + minimizedCss); res.send(
notice +
" " +
minimizedCss.replace("https://p.poketube.fun", config.p_url)
);
} else { } else {
// Serve the original file // Serve the original file
res.sendFile(req.params.id, { root: html_location }); res.sendFile(req.params.id, { root: html_location });
@ -134,20 +195,27 @@ module.exports = function (app, config, renderTemplate) {
} }
}); });
app.get("/static/:id", (req, res) => { app.get("/game-hub", function (req, res) {
renderTemplate(res, req, "gamehub.ejs", {
game: req.query.game,
});
});
app.get("/static/:id", (req, res) => {
const id = req.params.id; const id = req.params.id;
if (id.endsWith(".css")) { if (id.endsWith(".css")) {
res.redirect("/css/" + id); res.redirect("/css/" + id);
} else if (id.endsWith(".js")) { } else if (id.endsWith(".js")) {
if (id.endsWith(".bundle.js")) { if (id.endsWith(".bundle.js")) {
const jsFiles = ['app.js', 'custom-css.js', 'emojis.js']; const jsFiles = ["app.js", "custom-css.js", "emojis.js"];
const combinedContent = jsFiles const combinedContent = jsFiles
.map((fileName) => { .map((fileName) => {
const filePath = path.join(html_location, fileName); const filePath = path.join(html_location, fileName);
return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf-8') : ''; return fs.existsSync(filePath)
? fs.readFileSync(filePath, "utf-8")
: "";
}) })
.join('\n'); .join("\n" + "\n");
const minimizedJs = require("uglify-js").minify(combinedContent).code; const minimizedJs = require("uglify-js").minify(combinedContent).code;
@ -186,7 +254,5 @@ app.get("/static/:id", (req, res) => {
} else { } else {
res.sendFile(id, { root: html_location }); res.sendFile(id, { root: html_location });
} }
}); });
}; };

View file

@ -139,6 +139,11 @@ function lightOrDark(color) {
} }
} }
function isDntEnabled(req) {
const dntHeader = req.header('DNT');
return dntHeader && (dntHeader === '1' || dntHeader === 'true');
}
function IsInArray(array, id) { function IsInArray(array, id) {
for (var i = 0; i < array.length; i++) { for (var i = 0; i < array.length; i++) {
if (array[i].id === id) return true; if (array[i].id === id) return true;
@ -178,19 +183,7 @@ module.exports = function (app, config, renderTemplate) {
}); });
app.get("/watch", async (req, res) => { app.get("/watch", async (req, res) => {
const { const { dm, region, hl, v, e, r, f, m, quality: q, a, universe, } = req.query;
dm,
region,
hl,
v,
e,
r,
f,
m,
quality: q,
a,
universe,
} = req.query;
if (!v) { if (!v) {
return res.redirect("/"); return res.redirect("/");
@ -212,6 +205,7 @@ module.exports = function (app, config, renderTemplate) {
core.video(v, contentlang, contentregion).then((data) => { core.video(v, contentlang, contentregion).then((data) => {
try { try {
const k = data?.video; const k = data?.video;
const channel_uploads = data?.channel_uploads
const json = data?.json; const json = data?.json;
const engagement = data?.engagement; const engagement = data?.engagement;
const inv_comments = data?.comments || "Disabled"; const inv_comments = data?.comments || "Disabled";
@ -223,6 +217,7 @@ module.exports = function (app, config, renderTemplate) {
d = desc.toString().replace(/\n/g, " <br> "); d = desc.toString().replace(/\n/g, " <br> ");
} }
const descriptionString = String(inv_vid?.description); const descriptionString = String(inv_vid?.description);
function extractInfo(regex) { function extractInfo(regex) {
@ -238,27 +233,34 @@ module.exports = function (app, config, renderTemplate) {
const reddit = extractInfo(REDDIT_REGEX); const reddit = extractInfo(REDDIT_REGEX);
const instagram = extractInfo(INSTAGRAM_REGEX); const instagram = extractInfo(INSTAGRAM_REGEX);
var proxyurl = config.p_url;
var vidurl = u.url; var vidurl = u.url;
var isvidious = u.isInvidiousURL; var isvidious = u.isInvidiousURL;
var mediaproxy = config.media_proxy
if (inv_vid?.genre === "Music") { if (inv_vid?.genre === "Music") {
var vidurl = u.losslessurl; var vidurl = u.losslessurl;
} }
if (inv_vid.author.endsWith(" - Topic")) {
var vidurl = "https://eu-proxy.poketube.fun"; var vidurl = "https://eu-proxy.poketube.fun";
var isvidious = true; var isvidious = true;
}
if (req.useragent.source.includes("Pardus")) { if (req.useragent.source.includes("Pardus")) {
var vidurl = "https://yt.sudovanilla.com"; var vidurl = "https://iv.ggtyler.dev";
var mediaproxy = "https://media-proxy.ashley0143.xyz"
var isvidious = true; var isvidious = true;
var isSchoolProxy = "";
} }
// unused
let badges = ""; let badges = "";
let comments = ""; let comments = "";
let nnn = ""; let nnn = "";
const dnt_val = isDntEnabled(req)
if ( if (
inv_vid?.error === inv_vid?.error ===
"The uploader has not made this video available in your country" || "The uploader has not made this video available in your country" ||
@ -299,15 +301,20 @@ module.exports = function (app, config, renderTemplate) {
twitter, twitter,
k, k,
dm, dm,
media_proxy_url: config.media_proxy, proxyurl,
media_proxy_url: mediaproxy,
instagram, instagram,
useragent: req.useragent, useragent: req.useragent,
verify, verify,
discord, discord,
turntomins,
twitch, twitch,
dnt_val,
reddit, reddit,
channel_uploads,
secure, secure,
process, process,
isSchoolProxy,
sha384, sha384,
lightOrDark, lightOrDark,
isMobile: req.useragent.isMobile, isMobile: req.useragent.isMobile,
@ -342,8 +349,7 @@ module.exports = function (app, config, renderTemplate) {
const { v, e, r, f, t, quality: q } = req.query; const { v, e, r, f, t, quality: q } = req.query;
try { try {
const info = await modules.fetch("http://ip-api.com/json/"); var mediaproxy = config.media_proxy
const ip = await info.json();
const { const {
video: k, video: k,
@ -363,11 +369,13 @@ module.exports = function (app, config, renderTemplate) {
const u = await media_proxy(v); const u = await media_proxy(v);
const d = desc.toString().replace(/\n/g, " <br> "); const d = desc.toString().replace(/\n/g, " <br> ");
const comments = inv_comments || "Disabled"; const comments = inv_comments || "Disabled";
const channel_uploads = data?.channel_uploads
const templateData = { const templateData = {
color, color,
color2, color2,
engagement, engagement,
channel_uploads,
u: u.url, u: u.url,
video: json, video: json,
date: k.Video.uploadDate, date: k.Video.uploadDate,
@ -378,11 +386,12 @@ module.exports = function (app, config, renderTemplate) {
lightOrDark, lightOrDark,
isMobile, isMobile,
tj, tj,
media_proxy_url: mediaproxy,
r, r,
qua: q, qua: q,
isvidious: u.isInvidiousURL, isvidious: u.isInvidiousURL,
inv: comments, inv: comments,
ip, turntomins,
convert, convert,
linkify, linkify,
wiki, wiki,

View file

@ -10,7 +10,6 @@ const { toJson } = require("xml2json");
const { curly } = require("node-libcurl"); const { curly } = require("node-libcurl");
const getdislikes = require("../libpoketube/libpoketube-dislikes.js"); const getdislikes = require("../libpoketube/libpoketube-dislikes.js");
const getColors = require("get-image-colors"); const getColors = require("get-image-colors");
const wiki = require("wikipedia");
/** /**
* Class representing PokeTube's core functionality. * Class representing PokeTube's core functionality.
@ -79,8 +78,8 @@ class PokeTubeCore {
try { try {
const [invComments, videoInfo, videoData] = await Promise.all([ const [invComments, videoInfo, videoData] = await Promise.all([
fetch(`${this.config.invapi}/comments/${v}?hl=${contentlang}&region=${contentregion}&h=${btoa(Date.now())}`).then((res) => res.text()), fetch(`${this.config.invapi_alt}/comments/${v}?hl=${contentlang}&region=${contentregion}&h=${btoa(Date.now())}`).then((res) => res.text()),
fetch(`${this.config.invapi}/videos/${v}?hl=${contentlang}&region=${contentregion}&h=${btoa(Date.now())}`).then((res) => res.text()), fetch(`${this.config.invapi_alt}/videos/${v}?hl=${contentlang}&region=${contentregion}&h=${btoa(Date.now())}`).then((res) => res.text()),
curly curly
.get(`${this.config.tubeApi}video?v=${v}`, { .get(`${this.config.tubeApi}video?v=${v}`, {
httpHeader: Object.entries(headers).map(([k, v]) => `${k}: ${v}`), httpHeader: Object.entries(headers).map(([k, v]) => `${k}: ${v}`),
@ -96,6 +95,11 @@ class PokeTubeCore {
const vid = await this.getJson(videoInfo); const vid = await this.getJson(videoInfo);
const { json, video } = videoData; const { json, video } = videoData;
const channel_uploads = await fetch(
`${this.config.invapi_alt}/channels/${vid.authorId}?hl=${contentlang}&region=${contentregion}`
);
const p = this.getJson(await channel_uploads.text());
if (!vid) { if (!vid) {
console.log( console.log(
`Sorry nya, we couldn't find any information about that video qwq` `Sorry nya, we couldn't find any information about that video qwq`
@ -115,14 +119,15 @@ class PokeTubeCore {
video, video,
vid, vid,
comments, comments,
channel_uploads:p,
engagement: fe.engagement, engagement: fe.engagement,
wiki: "", wiki: "",
desc: "", desc: "",
color: await getColors( color: await getColors(
`https://i.ytimg.com/vi/${v}/hqdefault.jpg?sqp=${this.sqp}` `https://vid.puffyan.us/vi/${v}/hqdefault.jpg?sqp=${this.sqp}`
).then((colors) => colors[0].hex()), ).then((colors) => colors[0].hex()),
color2: await getColors( color2: await getColors(
`https://i.ytimg.com/vi/${v}/hqdefault.jpg?sqp=${this.sqp}` `https://vid.puffyan.us/vi/${v}/hqdefault.jpg?sqp=${this.sqp}`
).then((colors) => colors[1].hex()), ).then((colors) => colors[1].hex()),
}, },
timestamp: Date.now(), timestamp: Date.now(),
@ -166,6 +171,7 @@ class PokeTubeCore {
const pokeTubeApiCore = new PokeTubeCore({ const pokeTubeApiCore = new PokeTubeCore({
tubeApi: "https://inner-api.poketube.fun/api/", tubeApi: "https://inner-api.poketube.fun/api/",
invapi: "https://invid-api.poketube.fun/api/v1", invapi: "https://invid-api.poketube.fun/api/v1",
invapi_alt: "https://iv.ggtyler.dev/api/v1",
dislikes: "https://returnyoutubedislikeapi.com/votes?videoId=", dislikes: "https://returnyoutubedislikeapi.com/votes?videoId=",
t_url: "https://t.poketube.fun/", t_url: "https://t.poketube.fun/",
}); });

View file

@ -2,7 +2,7 @@
PokeTube is a Free/Libre youtube front-end ! PokeTube is a Free/Libre youtube front-end !
Copyright (C) 2021-2022 POKETUBE Copyright (C) 2021-2024 POKETUBE
This file is Licensed under LGPL-3.0-or-later. Poketube itself is GPL, Only this file is LGPL. This file is Licensed under LGPL-3.0-or-later. Poketube itself is GPL, Only this file is LGPL.
@ -12,6 +12,11 @@
*/ */
/**
* Checks if a string is a valid JSON.
* @param {string} str - The string to be checked.
* @returns {boolean} - Returns true if the string is a valid JSON, otherwise false.
*/
function IsJsonString(str) { function IsJsonString(str) {
try { try {
JSON.parse(str); JSON.parse(str);
@ -21,50 +26,87 @@ function IsJsonString(str) {
return true; return true;
} }
/**
* Converts a number into a compact string representation using the en-GB locale.
* @param {number} value - The number to be converted.
* @returns {string} - The compact string representation of the number.
*/
function convert(value) { function convert(value) {
return new Intl.NumberFormat("en-GB", { return new Intl.NumberFormat("en-GB", {
notation: "compact", notation: "compact",
}).format(value); }).format(value);
} }
/**
* Extracts the first line of text before the first occurrence of "<br>".
* @param {string} text - The input text.
* @returns {string} - The first line of text before "<br>", or the entire text if no "<br>" is found.
*/
function getFirstLine(text) { function getFirstLine(text) {
var index = text?.indexOf("<br> "); var index = text?.indexOf("<br> ");
if (index === -1) index = undefined; if (index === -1) index = undefined;
return text?.substring(0, index); return text?.substring(0, index);
} }
/**
* Capitalizes the first letter of a string.
* @param {string} string - The input string.
* @returns {string} - The string with the first letter capitalized.
*/
function capitalizeFirstLetter(string) { function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1); return string.charAt(0).toUpperCase() + string.slice(1);
} }
/**
* Converts time in seconds to a formatted time string (hh:mm:ss or mm:ss).
* If time is 00:00, returns "LIVE".
* @param {number} time - The time in seconds.
* @returns {string} - The formatted time string.
*/
function turntomins(time) { function turntomins(time) {
var minutes = Math.floor(time / 60); var hours = Math.floor(time / 3600);
var remainingSeconds = time - hours * 3600;
var seconds = time - minutes * 60; var minutes = Math.floor(remainingSeconds / 60);
var seconds = remainingSeconds - minutes * 60;
function str_pad_left(string, pad, length) { function str_pad_left(string, pad, length) {
return (new Array(length + 1).join(pad) + string).slice(-length); return (new Array(length + 1).join(pad) + string).slice(-length);
} }
if (hours > 0) {
var finalTime =
str_pad_left(hours, "0", 2) +
":" +
str_pad_left(minutes, "0", 2) +
":" +
str_pad_left(seconds, "0", 2);
} else {
if (minutes === 0 && seconds === 0) {
return "LIVE";
} else {
var finalTime = var finalTime =
str_pad_left(minutes, "0", 2) + ":" + str_pad_left(seconds, "0", 2); str_pad_left(minutes, "0", 2) + ":" + str_pad_left(seconds, "0", 2);
}
}
return finalTime; return finalTime;
}; }
/** /**
* Returns a random number between min (inclusive) and max (exclusive) * Returns a random floating point number within the specified range.
* @param {number} min - The minimum value of the range (inclusive).
* @param {number} max - The maximum value of the range (exclusive).
* @returns {number} - A random floating point number within the specified range.
*/ */
function getRandomArbitrary(min, max) { function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min; return Math.random() * (max - min) + min;
} }
/** /**
* Returns a random integer between min (inclusive) and max (inclusive). * Returns a random integer within the specified range.
* The value is no lower than min (or the next integer greater than min * @param {number} min - The minimum value of the range (inclusive).
* if min isn't an integer) and no greater than max (or the next integer * @param {number} max - The maximum value of the range (inclusive).
* lower than max if max isn't an integer). * @returns {number} - A random integer within the specified range.
* Using Math.round() will give you a non-uniform distribution!
*/ */
function getRandomInt(min, max) { function getRandomInt(min, max) {
min = Math.ceil(min); min = Math.ceil(min);
@ -72,6 +114,12 @@ function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min; return Math.floor(Math.random() * (max - min + 1)) + min;
} }
/**
* Increases or decreases the brightness of a hexadecimal color code.
* @param {string} hex - The hexadecimal color code.
* @param {number} percent - The percentage by which to adjust the brightness (positive for increase, negative for decrease).
* @returns {string} - The modified hexadecimal color code.
*/
function increase_brightness(hex, percent){ function increase_brightness(hex, percent){
// strip the leading # if it's there // strip the leading # if it's there
hex = hex.replace(/^\s*#|\s*$/g, ''); hex = hex.replace(/^\s*#|\s*$/g, '');
@ -91,12 +139,92 @@ function increase_brightness(hex, percent){
((0|(1<<8) + b + (256 - b) * percent / 100).toString(16)).substr(1); ((0|(1<<8) + b + (256 - b) * percent / 100).toString(16)).substr(1);
} }
/**
* Converts an array to an object with numeric keys.
* @param {Array} arr - The input array.
* @returns {Object} - The resulting object with numeric keys.
*/
function toObject(arr) {
var rv = {};
for (var i = 0; i < arr.length; ++i) if (arr[i] !== undefined) rv[i] = arr[i];
return rv;
}
/**
* Determines if a color is light or dark.
* @param {string} color - The color code in hexadecimal or RGB format.
* @returns {string} - Returns "light" if the color is light, otherwise "dark".
*/
function lightOrDark(color) {
// Variables for red, green, blue values
var r, g, b, hsp;
// Check the format of the color, HEX or RGB?
if (color.match(/^rgb/)) {
// If RGB --> store the red, green, blue values in separate variables
color = color.match(
/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/
);
r = color[1];
g = color[2];
b = color[3];
} else {
// If hex --> Convert it to RGB: http://gist.github.com/983661
color = +("0x" + color.slice(1).replace(color.length < 5 && /./g, "$&$&"));
r = color >> 16;
g = (color >> 8) & 255;
b = color & 255;
}
// HSP (Highly Sensitive Poo) equation from http://alienryderflex.com/hsp.html
hsp = Math.sqrt(0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b));
// Using the HSP value, determine whether the color is light or dark
if (hsp > 127.5) {
return "light";
} else {
return "dark";
}
}
/**
* Checks if an element with a specific ID exists in an array of objects.
* @param {Array} array - The array of objects to be searched.
* @param {string} id - The ID to search for.
* @returns {boolean} - Returns true if the ID exists in the array, otherwise false.
*/
function IsInArray(array, id) {
for (var i = 0; i < array.length; i++) {
if (array[i].id === id) return true;
}
return false;
}
/**
* Parses a JSON string into a JavaScript object.
* @param {string} str - The JSON string to be parsed.
* @returns {(Object|boolean)} - Returns the parsed JavaScript object if successful, otherwise false.
*/
function getJson(str) {
try {
return JSON.parse(str);
} catch {
return false;
}
}
module.exports = { module.exports = {
IsJsonString, IsJsonString,
convert, convert,
getFirstLine, getFirstLine,
getRandomArbitrary, getRandomArbitrary,
getRandomInt, getJson,
capitalizeFirstLetter, lightOrDark,
turntomins toObject,
IsInArray,
getRandomInt,
capitalizeFirstLetter,
turntomins
}; };