Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(Freek): add presence #9014

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions websites/F/Freek/iframe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const iframe = new iFrame();

iframe.on("UpdateData", async () => {
if (document.querySelector("video")) {
const video: HTMLVideoElement = document.querySelector("video");
if (video && !isNaN(video.duration)) {
iframe.send({
duration: video.duration,
currentTime: video.currentTime,
paused: video.paused,
});
}
}
});
56 changes: 56 additions & 0 deletions websites/F/Freek/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"$schema": "https://schemas.premid.app/metadata/1.12",
"apiVersion": 1,
"author": {
"id": "567885938160697377",
"name": "Anaxes"
},
"service": "Freek",
"description": {
"en": "Freek.to is a free movie, tv show and more streaming application, supporting high quality streams and friendly UI and UX experiences."
},
"url": "freek.to",
"version": "1.0.0",
"logo": "https://i.imgur.com/wmHltUl.png",
"thumbnail": "https://i.imgur.com/YU9QbjM.png",
"color": "#121212",
"category": "videos",
"tags": [
"movies",
"anime",
"tv",
"manga"
],
"iframe": true,
"iFrameRegExp": ".*",

Check warning on line 25 in websites/F/Freek/metadata.json

View workflow job for this annotation

GitHub Actions / Presence Validator

Presence (Freek) has metadata.iFrameRegExp set to '.*', please change this if possible
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you able to use a more narrow regex?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really sure what it iframe regex is. Is it just limiting the domain sources of the iframe?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, right now .* (meaning anything), will make the iframe.js script load into all iframes, regardless of URL.

It would be nice if you could limit it to a certain domain or path for example, so it doesn't load into Google Analytics iframe for example.

"settings": [
{
"id": "lang",
"multiLanguage": true
},
{
"id": "privacy",
"title": "Privacy Mode",
"icon": "fas fa-user-secret",
"value": false
},
{
"id": "timestamp",
"title": "Show Timestamps",
"icon": "fad fa-stopwatch",
"value": true,
"if": {
"privacy": false
}
},
{
"id": "buttons",
"title": "Show Buttons",
"icon": "fas fa-compress-arrows-alt",
"value": true,
"if": {
"privacy": false
}
}
]
}
241 changes: 241 additions & 0 deletions websites/F/Freek/presence.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
const presence = new Presence({
clientId: "1321328844497752124",
}),
browsingTimestamp = Math.floor(Date.now() / 1000),
strings = presence.getStrings({
playing: "general.playing",
pause: "general.paused",
home: "general.viewHome",
search: "general.searchFor",
browse: "general.browsing",
reading: "general.reading",
buttonViewPage: "general.buttonViewPage",
buttonViewEpisode: "general.buttonViewEpisode",
buttonWatchAnime: "general.buttonWatchAnime",
buttonWatchMovie: "general.buttonWatchMovie",
buttonViewSeries: "general.buttonViewSeries",
});

const enum Assets {
Logo = "https://i.imgur.com/wmHltUl.png",
}

let video = {
duration: 0,
currentTime: 0,
paused: true,
};

presence.on(
"iFrameData",
(data: { duration: number; currentTime: number; paused: boolean }) => {
video = data;
}
);

presence.on("UpdateData", async () => {
let presenceData: PresenceData = {
largeImageKey: Assets.Logo,
startTimestamp: browsingTimestamp,
type: ActivityType.Watching,
details: "Unsupported Page",
};

const { href, pathname } = document.location,
[showTimestamp, showButtons, privacy] = await Promise.all([
presence.getSetting<boolean>("timestamp"),
presence.getSetting<boolean>("buttons"),
presence.getSetting<boolean>("privacy"),
]);

if (privacy) {
presenceData.details = "Watching Freek";
presence.setActivity(presenceData);
return;
}

const pages: Record<string, PresenceData> = {
"/": {
details: (await strings).home,
smallImageKey: Assets.Viewing,
},
"/watchlist": {
details: `${(await strings).browse} Watchlist`,
smallImageKey: Assets.Viewing,
},
"/history": {
details: `${(await strings).browse} History`,
smallImageKey: Assets.Viewing,
},
"/settings": {
details: `${(await strings).browse} Settings`,
smallImageKey: Assets.Viewing,
},
};

for (const [path, data] of Object.entries(pages))
if (pathname.includes(path)) presenceData = { ...presenceData, ...data };

const pageNumber = href.includes("?page=") ? href.split("?page=")[1] : 1,
searchInput = document.querySelector("input")
? document.querySelector("input").getAttribute("value")
: null,
searchResults = document.querySelector(
"div.flex.items-center.justify-between > span.flex.items-center.gap-1"
)?.textContent,
steamTitle = document.querySelector("div#right-header > div")
? document.querySelector("div#right-header > div")?.textContent
: document.querySelector("div.flex > span.flex-grow")?.textContent;

if (pathname.includes("/watch/")) {
const [startTimestamp, endTimestamp] = presence.getTimestamps(
Math.floor(video.currentTime),
Math.floor(video.duration)
),
episodeName = document.querySelector(
"div.pointer-events-none > button > div > span.font-light"
),
episodeNumber = href.includes("?ep=") ? href.split("?ep=")[1] : 1;

switch (pathname.split("/watch")[1].split("/")[1]) {
case "movie":
presenceData.details = steamTitle;
presenceData.state = `⭐ ${
document.querySelector("div.flex > div.false > span.rounded-md")
?.textContent
} 🗓️ ${
document.querySelectorAll("div.flex.flex-col > span")[3]?.textContent
}`;
presenceData.largeImageKey = document
.querySelector(
"div.flex > div.false > span.lazy-load-image-background > img"
)
?.getAttribute("src");
presenceData.smallImageKey = video.paused ? Assets.Pause : Assets.Play;
presenceData.smallImageText = video.paused
? (await strings).pause
: (await strings).playing;
presenceData.buttons = [
{
label: (await strings).buttonWatchMovie,
url: href,
},
];
break;
case "tv":
case "anime":
presenceData.details = steamTitle;
presenceData.state = `📺 ${
episodeName
? `${episodeNumber}. ${episodeName?.textContent}`
: `Episode ${episodeNumber}`
}`;
presenceData.largeImageKey = document
.querySelector(
"div.flex > div.false > span.lazy-load-image-background > img"
)
?.getAttribute("src");
presenceData.smallImageKey = video.paused ? Assets.Pause : Assets.Play;
presenceData.smallImageText = video.paused
? (await strings).pause
: (await strings).playing;
presenceData.buttons = [
{
label: (await strings).buttonViewEpisode,
url: href,
},
];
break;
default:
presenceData.details = "Watching a Video";
break;
}

if (showTimestamp) {
[presenceData.startTimestamp, presenceData.endTimestamp] = [
startTimestamp,
endTimestamp,
];
}

if (video.paused) delete presenceData.endTimestamp;
} else if (pathname.includes("/read/")) {
const mangaName = document.querySelector(
"div.pointer-events-none > button > div > span.font-light"
);

presenceData.details = steamTitle;
presenceData.state = `📺 ${
mangaName
? `${mangaName?.textContent}`
: `Chapter ${href.includes("?ep=") ? href.split("?ep=")[1] : 1}`
}`;
presenceData.largeImageKey = document
.querySelector(
"div.flex > div.false > span.lazy-load-image-background > img"
)
?.getAttribute("src");
presenceData.smallImageKey = Assets.Reading;
presenceData.smallImageText = (await strings).reading;
presenceData.buttons = [
{
label: (await strings).buttonViewSeries,
url: href,
},
];
} else if (pathname.includes("/explore")) {
presenceData.state = `Page ${pageNumber} - ${searchResults}`;
presenceData.smallImageKey = Assets.Viewing;

if (searchInput) {
presenceData.state = `Query: ${searchInput}`;
presenceData.smallImageKey = Assets.Search;
presenceData.smallImageText = `Page ${pageNumber} - ${searchResults}`;
}

switch (pathname.split("/explore/")[1]) {
case "movie":
presenceData.details = "Exploring Movies";

if (searchInput)
presenceData.details = `${(await strings).search} Movies`;

break;
case "tv":
presenceData.details = "Exploring TV Shows";

if (searchInput)
presenceData.details = `${(await strings).search} TV Shows`;
break;
case "anime":
presenceData.details = "Exploring Anime";
if (searchInput)
presenceData.details = `${(await strings).search} Anime`;
break;
case "manga":
presenceData.details = "Exploring Manga";
if (searchInput)
presenceData.details = `${(await strings).search} Manga`;
break;
default:
presenceData.details = "Exploring";
break;
}
} else if (pathname.includes("/search")) {
presenceData.details = `Searching for ${
href.includes("type=") ? href.split("type=")[1] : "movie"
}`;
presenceData.state = `Query: ${document
.querySelector("input")
.getAttribute("value")}`;
presenceData.smallImageKey = Assets.Search;
presenceData.smallImageText = `Page ${
href.includes("page=") ? href.split("page=")[1].split("&type")[0] : 1
} - ${searchResults}`;
}

if (!showButtons && presenceData.buttons) delete presenceData.buttons;

if (presenceData.details) presence.setActivity(presenceData);
else presence.setActivity();
});
Loading