mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-11 10:31:47 +00:00
feat: Support disabling scrobbling for non-music content (#1665)
Co-authored-by: JellyBrick <shlee1503@naver.com>
This commit is contained in:
@ -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
|
> (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)
|
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
|
- **Lumia Stream**: Adds [Lumia Stream](https://lumiastream.com/) support
|
||||||
|
|
||||||
|
|||||||
@ -32,6 +32,7 @@ export interface DefaultConfig {
|
|||||||
proxy: string;
|
proxy: string;
|
||||||
startingPage: string;
|
startingPage: string;
|
||||||
overrideUserAgent: boolean;
|
overrideUserAgent: boolean;
|
||||||
|
usePodcastParticipantAsArtist: boolean;
|
||||||
themes: string[];
|
themes: string[];
|
||||||
};
|
};
|
||||||
plugins: Record<string, unknown>;
|
plugins: Record<string, unknown>;
|
||||||
@ -66,6 +67,7 @@ const defaultConfig: DefaultConfig = {
|
|||||||
proxy: '',
|
proxy: '',
|
||||||
startingPage: '',
|
startingPage: '',
|
||||||
overrideUserAgent: false,
|
overrideUserAgent: false,
|
||||||
|
usePodcastParticipantAsArtist: false,
|
||||||
themes: [],
|
themes: [],
|
||||||
},
|
},
|
||||||
'plugins': {},
|
'plugins': {},
|
||||||
|
|||||||
@ -572,6 +572,7 @@
|
|||||||
"scrobbler": {
|
"scrobbler": {
|
||||||
"description": "Add scrobbling support (etc. last.fm, Listenbrainz)",
|
"description": "Add scrobbling support (etc. last.fm, Listenbrainz)",
|
||||||
"menu": {
|
"menu": {
|
||||||
|
"scrobble-other-media": "Scrobble other media",
|
||||||
"lastfm": {
|
"lastfm": {
|
||||||
"api-settings": "Last.fm API Settings"
|
"api-settings": "Last.fm API Settings"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -6,6 +6,12 @@ import { backend } from './main';
|
|||||||
|
|
||||||
export interface ScrobblerPluginConfig {
|
export interface ScrobblerPluginConfig {
|
||||||
enabled: boolean,
|
enabled: boolean,
|
||||||
|
/**
|
||||||
|
* Attempt to scrobble other video types (e.g. Podcasts, normal YouTube videos)
|
||||||
|
*
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
scrobble_other_media: boolean,
|
||||||
scrobblers: {
|
scrobblers: {
|
||||||
lastfm: {
|
lastfm: {
|
||||||
/**
|
/**
|
||||||
@ -64,6 +70,7 @@ export interface ScrobblerPluginConfig {
|
|||||||
|
|
||||||
export const defaultConfig: ScrobblerPluginConfig = {
|
export const defaultConfig: ScrobblerPluginConfig = {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
scrobble_other_media: true,
|
||||||
scrobblers: {
|
scrobblers: {
|
||||||
lastfm: {
|
lastfm: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
|||||||
@ -51,6 +51,11 @@ export const backend = createBackend<{
|
|||||||
clearTimeout(scrobbleTimer);
|
clearTimeout(scrobbleTimer);
|
||||||
if (!songInfo.isPaused) {
|
if (!songInfo.isPaused) {
|
||||||
const configNonnull = this.config!;
|
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
|
// 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);
|
const scrobbleTime = Math.min(Math.ceil(songInfo.songDuration / 2), 4 * 60);
|
||||||
if (scrobbleTime > (songInfo.elapsedSeconds ?? 0)) {
|
if (scrobbleTime > (songInfo.elapsedSeconds ?? 0)) {
|
||||||
|
|||||||
@ -79,6 +79,15 @@ export const onMenu = async ({
|
|||||||
const config = await getConfig();
|
const config = await getConfig();
|
||||||
|
|
||||||
return [
|
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',
|
label: 'Last.fm',
|
||||||
submenu: [
|
submenu: [
|
||||||
|
|||||||
@ -164,12 +164,6 @@ export default (api: YoutubePlayer) => {
|
|||||||
data.videoDetails.elapsedSeconds = 0;
|
data.videoDetails.elapsedSeconds = 0;
|
||||||
data.videoDetails.isPaused = false;
|
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);
|
window.ipcRenderer.send('ytmd:video-src-changed', data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,6 +7,26 @@ import config from '@/config';
|
|||||||
|
|
||||||
import type { GetPlayerResponse } from '@/types/get-player-response';
|
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 {
|
export interface SongInfo {
|
||||||
title: string;
|
title: string;
|
||||||
artist: string;
|
artist: string;
|
||||||
@ -21,6 +41,7 @@ export interface SongInfo {
|
|||||||
album?: string | null;
|
album?: string | null;
|
||||||
videoId: string;
|
videoId: string;
|
||||||
playlistId?: string;
|
playlistId?: string;
|
||||||
|
mediaType: MediaType;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grab the native image using the src
|
// Grab the native image using the src
|
||||||
@ -61,6 +82,7 @@ const handleData = async (
|
|||||||
album: undefined,
|
album: undefined,
|
||||||
videoId: '',
|
videoId: '',
|
||||||
playlistId: '',
|
playlistId: '',
|
||||||
|
mediaType: MediaType.Audio,
|
||||||
} satisfies SongInfo;
|
} satisfies SongInfo;
|
||||||
|
|
||||||
const microformat = data.microformat?.microformatDataRenderer;
|
const microformat = data.microformat?.microformatDataRenderer;
|
||||||
@ -84,6 +106,28 @@ const handleData = async (
|
|||||||
songInfo.videoId = videoDetails.videoId;
|
songInfo.videoId = videoDetails.videoId;
|
||||||
songInfo.album = data?.videoDetails?.album; // Will be undefined if video exist
|
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;
|
const thumbnails = videoDetails.thumbnail?.thumbnails;
|
||||||
songInfo.imageSrc = thumbnails.at(-1)?.url.split('?')[0];
|
songInfo.imageSrc = thumbnails.at(-1)?.url.split('?')[0];
|
||||||
if (songInfo.imageSrc) songInfo.image = await getImage(songInfo.imageSrc);
|
if (songInfo.imageSrc) songInfo.image = await getImage(songInfo.imageSrc);
|
||||||
|
|||||||
Reference in New Issue
Block a user