From e3ad804dc48d161bf4495fb849ce4a5a68e74fff Mon Sep 17 00:00:00 2001 From: Alex <69764315+Serial-ATA@users.noreply.github.com> Date: Wed, 31 Jan 2024 03:41:55 -0500 Subject: [PATCH] feat: Support disabling scrobbling for non-music content (#1665) Co-authored-by: JellyBrick --- README.md | 2 +- src/config/defaults.ts | 2 ++ src/i18n/resources/en.json | 1 + src/plugins/scrobbler/index.ts | 7 +++++ src/plugins/scrobbler/main.ts | 5 ++++ src/plugins/scrobbler/menu.ts | 9 +++++++ src/providers/song-info-front.ts | 6 ----- src/providers/song-info.ts | 44 ++++++++++++++++++++++++++++++++ 8 files changed, 69 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 10504701..b5cf6aef 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ winget install th-ch.YouTubeMusic > (see [this post](https://github.com/th-ch/youtube-music/issues/410#issuecomment-952060709) if you have problem accessing the menu after enabling this plugin and hide-menu option) -- [**Last.fm**](https://www.last.fm/): Scrobbles support +- **Scrobbler**: Adds scrobbling support for [Last.fm](https://www.last.fm/) and [ListenBrainz](https://listenbrainz.org/) - **Lumia Stream**: Adds [Lumia Stream](https://lumiastream.com/) support diff --git a/src/config/defaults.ts b/src/config/defaults.ts index a09f3194..dcd56128 100644 --- a/src/config/defaults.ts +++ b/src/config/defaults.ts @@ -32,6 +32,7 @@ export interface DefaultConfig { proxy: string; startingPage: string; overrideUserAgent: boolean; + usePodcastParticipantAsArtist: boolean; themes: string[]; }; plugins: Record; @@ -66,6 +67,7 @@ const defaultConfig: DefaultConfig = { proxy: '', startingPage: '', overrideUserAgent: false, + usePodcastParticipantAsArtist: false, themes: [], }, 'plugins': {}, diff --git a/src/i18n/resources/en.json b/src/i18n/resources/en.json index 579378b8..f0a120f3 100644 --- a/src/i18n/resources/en.json +++ b/src/i18n/resources/en.json @@ -572,6 +572,7 @@ "scrobbler": { "description": "Add scrobbling support (etc. last.fm, Listenbrainz)", "menu": { + "scrobble-other-media": "Scrobble other media", "lastfm": { "api-settings": "Last.fm API Settings" }, diff --git a/src/plugins/scrobbler/index.ts b/src/plugins/scrobbler/index.ts index 26c84a9c..6b0ae881 100644 --- a/src/plugins/scrobbler/index.ts +++ b/src/plugins/scrobbler/index.ts @@ -6,6 +6,12 @@ import { backend } from './main'; export interface ScrobblerPluginConfig { enabled: boolean, + /** + * Attempt to scrobble other video types (e.g. Podcasts, normal YouTube videos) + * + * @default true + */ + scrobble_other_media: boolean, scrobblers: { lastfm: { /** @@ -64,6 +70,7 @@ export interface ScrobblerPluginConfig { export const defaultConfig: ScrobblerPluginConfig = { enabled: false, + scrobble_other_media: true, scrobblers: { lastfm: { enabled: false, diff --git a/src/plugins/scrobbler/main.ts b/src/plugins/scrobbler/main.ts index 8ce86028..96537356 100644 --- a/src/plugins/scrobbler/main.ts +++ b/src/plugins/scrobbler/main.ts @@ -51,6 +51,11 @@ export const backend = createBackend<{ clearTimeout(scrobbleTimer); if (!songInfo.isPaused) { const configNonnull = this.config!; + // Scrobblers normally have no trouble working with official music videos + if (!configNonnull.scrobble_other_media && (songInfo.mediaType !== 'AUDIO' && songInfo.mediaType !== 'ORIGINAL_MUSIC_VIDEO')) { + return; + } + // Scrobble when the song is halfway through, or has passed the 4-minute mark const scrobbleTime = Math.min(Math.ceil(songInfo.songDuration / 2), 4 * 60); if (scrobbleTime > (songInfo.elapsedSeconds ?? 0)) { diff --git a/src/plugins/scrobbler/menu.ts b/src/plugins/scrobbler/menu.ts index 1b3e6e33..1c663225 100644 --- a/src/plugins/scrobbler/menu.ts +++ b/src/plugins/scrobbler/menu.ts @@ -79,6 +79,15 @@ export const onMenu = async ({ const config = await getConfig(); return [ + { + label: t('plugins.scrobbler.menu.scrobble-other-media'), + type: 'checkbox', + checked: Boolean(config.scrobble_other_media), + click(item) { + config.scrobble_other_media = item.checked; + setConfig(config); + }, + }, { label: 'Last.fm', submenu: [ diff --git a/src/providers/song-info-front.ts b/src/providers/song-info-front.ts index b75c249b..99481670 100644 --- a/src/providers/song-info-front.ts +++ b/src/providers/song-info-front.ts @@ -164,12 +164,6 @@ export default (api: YoutubePlayer) => { data.videoDetails.elapsedSeconds = 0; data.videoDetails.isPaused = false; - // HACK: This is a workaround for "podcast" type video. GREAT JOB GOOGLE. - if (data.playabilityStatus.transportControlsConfig) { - data.videoDetails.author = - data.microformat.microformatDataRenderer.pageOwnerDetails.name; - } - window.ipcRenderer.send('ytmd:video-src-changed', data); } }; diff --git a/src/providers/song-info.ts b/src/providers/song-info.ts index 761ef3e0..a62cce00 100644 --- a/src/providers/song-info.ts +++ b/src/providers/song-info.ts @@ -7,6 +7,26 @@ import config from '@/config'; import type { GetPlayerResponse } from '@/types/get-player-response'; +enum MediaType { + /** + * Audio uploaded by the original artist + */ + Audio = 'AUDIO', + /** + * Official music video uploaded by the original artist + */ + OriginalMusicVideo = 'ORIGINAL_MUSIC_VIDEO', + /** + * Normal YouTube video uploaded by a user + */ + UserGeneratedContent = 'USER_GENERATED_CONTENT', + /** + * Podcast episode + */ + PodcastEpisode = 'PODCAST_EPISODE', + OtherVideo = 'OTHER_VIDEO', +} + export interface SongInfo { title: string; artist: string; @@ -21,6 +41,7 @@ export interface SongInfo { album?: string | null; videoId: string; playlistId?: string; + mediaType: MediaType; } // Grab the native image using the src @@ -61,6 +82,7 @@ const handleData = async ( album: undefined, videoId: '', playlistId: '', + mediaType: MediaType.Audio, } satisfies SongInfo; const microformat = data.microformat?.microformatDataRenderer; @@ -84,6 +106,28 @@ const handleData = async ( songInfo.videoId = videoDetails.videoId; songInfo.album = data?.videoDetails?.album; // Will be undefined if video exist + switch (videoDetails?.musicVideoType) { + case 'MUSIC_VIDEO_TYPE_ATV': + songInfo.mediaType = MediaType.Audio; + break; + case 'MUSIC_VIDEO_TYPE_OMV': + songInfo.mediaType = MediaType.OriginalMusicVideo; + break; + case 'MUSIC_VIDEO_TYPE_UGC': + songInfo.mediaType = MediaType.UserGeneratedContent; + break; + case 'MUSIC_VIDEO_TYPE_PODCAST_EPISODE': + songInfo.mediaType = MediaType.PodcastEpisode; + // HACK: Podcast's participant is not the artist + if (!config.get('options.usePodcastParticipantAsArtist')) { + songInfo.artist = cleanupName(data.microformat.microformatDataRenderer.pageOwnerDetails.name); + } + break; + default: + songInfo.mediaType = MediaType.OtherVideo; + break; + } + const thumbnails = videoDetails.thumbnail?.thumbnails; songInfo.imageSrc = thumbnails.at(-1)?.url.split('?')[0]; if (songInfo.imageSrc) songInfo.image = await getImage(songInfo.imageSrc);