Skip to content

Commit

Permalink
feat(YTyping): add YTyping presence (#9323)
Browse files Browse the repository at this point in the history
* Add files via upload

* fix lint error

* fix getTimestamps

* add presence state

* fix import

* fix lint error

* fix lint error

* fix lint error

* remove promise.all

* fix singlequote

* refactor rename function getPresenceData → generatePresenceData

* add buttons

* refactor simplify presence state handling

* fix single quote

* fix lint error

* fix lint error

* fix lint error

* add iFrameRegExp

* add button

* refactor iframe.ts

* remove privacy setting

* remove main page button

* add youtube button

* remove const

* remove document.location.hostname if
  • Loading branch information
Toshi7878 authored Feb 27, 2025
1 parent 27cb6b6 commit 66c55cb
Show file tree
Hide file tree
Showing 3 changed files with 256 additions and 0 deletions.
13 changes: 13 additions & 0 deletions websites/Y/YTyping/iframe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const iframe = new iFrame()

iframe.on('UpdateData', async () => {
const { currentTime, duration, paused } = document.querySelector<HTMLVideoElement>(
'video.video-stream.html5-main-video',
)!

iframe.send({
currentTime,
duration,
paused,
})
})
58 changes: 58 additions & 0 deletions websites/Y/YTyping/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"$schema": "https://schemas.premid.app/metadata/1.7",
"author": {
"name": "Toshi",
"id": "548698164874575875"
},
"service": "YTyping",
"description": {
"en": "A website where you can practice typing with YouTube music."
},
"url": "ytyping.net",
"version": "1.0.0",
"logo": "https://i.imgur.com/nnZqkNo.png",
"thumbnail": "https://i.imgur.com/ULpnvtt.png",
"color": "#3b5a7d",
"category": "games",
"tags": ["game", "music", "typing"],
"iframe": true,
"iFrameRegExp": "www[.]youtube[.]com[/]",
"settings": [
{
"id": "showCurrentSong",
"title": "Show Current Song",
"icon": "fas fa-music",
"value": true
},
{
"id": "showTypingState",
"title": "Show Typing State",
"icon": "fas fa-keyboard",
"value": true
},
{
"id": "showMediaState",
"title": "Show Media State",
"icon": "fas fa-play-circle",
"value": true
},
{
"id": "showTimestamp",
"title": "Show Timestamp",
"icon": "fas fa-clock",
"value": true
},
{
"id": "showCover",
"title": "Show Cover",
"icon": "fad fa-images",
"value": true
},
{
"id": "showButtons",
"title": "Show Buttons",
"icon": "fas fa-compress-arrows-alt",
"value": true
}
]
}
185 changes: 185 additions & 0 deletions websites/Y/YTyping/presence.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import { ActivityType, Assets, getTimestamps } from 'premid'

const presence = new Presence({
clientId: '1338529406905352212',
})

presence.on('UpdateData', async () => {
const presenceData = await generatePresenceData()

presence.setActivity(presenceData)
})

let _video = {
duration: 0,
currentTime: 0,
paused: true,
}
presence.on(
'iFrameData',
(data: unknown) => {
_video = data as typeof _video
},
)

function getTypePageInfo() {
const videoId = document
?.querySelector<HTMLMetaElement>('[name="article:youtube_id"]')
?.content ?? ''
const title = document
?.querySelector<HTMLMetaElement>('[name="article:title"]')
?.content ?? ''
const artist = document
?.querySelector<HTMLMetaElement>('[name="article:artist"]')
?.content ?? ''
const image = document
?.querySelector<HTMLMetaElement>('[property="og:image"]')
?.content ?? ''

return { title, artist, image, videoId }
}

const largeImageKey = 'https://i.imgur.com/nnZqkNo.png'
const browsingTimestamp = Math.floor(Date.now() / 1000)

async function generatePresenceData() {
const { pathname, href } = document.location
const [
showCurrentSong,
showTypingState,
showMediaState,
showTimestamp,
showCover,
showButtons,
] = await Promise.all([
presence.getSetting<boolean>('showCurrentSong'),
presence.getSetting<boolean>('showTypingState'),
presence.getSetting<boolean>('showMediaState'),
presence.getSetting<boolean>('showTimestamp'),
presence.getSetting<boolean>('showCover'),
presence.getSetting<boolean>('showButtons'),
])

switch (true) {
case pathname.includes('/type'):{
const map = getTypePageInfo()
const isPaused = _video.paused

const presenceData: PresenceData = {
type: isPaused ? ActivityType.Playing : ActivityType.Listening,
largeImageKey: showCover ? map.image : largeImageKey,
}

if (showMediaState) {
presenceData.smallImageKey = isPaused ? Assets.Pause : Assets.Play
presenceData.smallImageText = isPaused ? '停止中' : 'プレイ中'
}

if (showTimestamp) {
const [startTimestamp, endTimestamp] = getTimestamps(
Math.floor(_video.currentTime),
Math.floor(_video.duration),
)
presenceData.startTimestamp = startTimestamp
presenceData.endTimestamp = endTimestamp
}

if (showButtons) {
presenceData.buttons = [{
url: href,
label: 'YTypingを開く',
}, {
url: `https://youtu.be/${map.videoId}`,
label: 'YouTubeで聴く',
}]
}

if (showCurrentSong) {
presenceData.details = `${map.title} - ${map.artist}`
}

if (showTypingState) {
const playingNotifyText = document.querySelector('#playing_notify')?.textContent
const typeCount = document.querySelector('[label=\'type\']')?.textContent ?? 0
const kpm = document.querySelector('[label=\'kpm\']')?.textContent ?? 0

if (playingNotifyText?.includes('Replay')) {
presenceData.state = 'リプレイ視聴中'
}
else {
presenceData.state = `打鍵数:${typeCount} kpm:${kpm}`
}
}

return presenceData
}

case pathname.includes('/edit'):{
const iframeElement = document.querySelector('iframe')

const videoId = iframeElement?.src.split('/embed/')[1]?.split('?')[0] || ''
const videoThubnail = `https://i.ytimg.com/vi/${videoId}/mqdefault.jpg`
const isPaused = _video.paused

const presenceData: PresenceData = {
largeImageKey: showCover && videoId ? videoThubnail : largeImageKey,
type: isPaused ? ActivityType.Playing : ActivityType.Listening,
}

if (showTypingState) {
presenceData.state = '譜面編集中'
}

if (showMediaState) {
presenceData.smallImageKey = isPaused ? Assets.Pause : Assets.Play
presenceData.smallImageText = isPaused ? '停止中' : 'プレイ中'
}

if (showTimestamp) {
const [startTimestamp, endTimestamp] = getTimestamps(
Math.floor(_video.currentTime),
Math.floor(_video.duration),
)
presenceData.startTimestamp = startTimestamp
presenceData.endTimestamp = endTimestamp
}

if (showCurrentSong) {
presenceData.details = iframeElement?.title ?? ''
}

if (showButtons) {
presenceData.buttons = [{
url: `https://youtu.be/${videoId}`,
label: 'YouTubeで聴く',
}]
}

return presenceData
}

default: {
const presenceData: PresenceData = {
startTimestamp: browsingTimestamp,
largeImageKey,
type: ActivityType.Playing,
}

switch (true) {
case pathname.includes('/timeline'):
presenceData.state = 'タイムライン'
break

case pathname.includes('/user'):
presenceData.state = 'ユーザーページ'
break

default:
presenceData.state = '待機中'
break
}

return presenceData
}
}
}

0 comments on commit 66c55cb

Please sign in to comment.