Skip to content

Commit

Permalink
Merge pull request #1665 from nukeop/feature/spotify-meta-provider
Browse files Browse the repository at this point in the history
Spotify meta provider
  • Loading branch information
nukeop authored Aug 15, 2024
2 parents 234f676 + e9bf3d0 commit 315ad9b
Show file tree
Hide file tree
Showing 55 changed files with 1,950 additions and 281 deletions.
1 change: 1 addition & 0 deletions packages/app/app/actions/actionTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export enum Search {
ARTIST_RELEASES_SEARCH_ERROR = 'ARTIST_RELEASES_SEARCH_ERROR',
LASTFM_TRACK_SEARCH_START = 'LASTFM_TRACK_SEARCH_START',
LASTFM_TRACK_SEARCH_SUCCESS = 'LASTFM_TRACK_SEARCH_SUCCESS',
TRACK_SEARCH_SUCCESS = 'TRACK_SEARCH_SUCCESS',
YOUTUBE_PLAYLIST_SEARCH_START = 'YOUTUBE_PLAYLIST_SEARCH_START',
YOUTUBE_PLAYLIST_SEARCH_SUCCESS = 'YOUTUBE_PLAYLIST_SEARCH_SUCCESS',
YOUTUBE_LIVESTREAM_SEARCH_START = 'YOUTUBE_LIVESTREAM_SEARCH_START',
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { store } from '@nuclear/core';
import UserPlugin from '../structs/userPlugin';
import { error } from './toasts';
import { createApi } from '@nuclear/core';
import { find, forEach, isNil } from 'lodash';

export const CREATE_PLUGINS = 'CREATE_PLUGINS';
export const SELECT_STREAM_PROVIDER = 'SELECT_STREAM_PROVIDER';
Expand Down Expand Up @@ -40,8 +41,8 @@ export function createPlugins (pluginClasses) {

const categories = Object.keys(pluginClasses);
const selected = {};
_.forEach(categories, category => {
const selectedForCategory = _.find(plugins[category], { isDefault: true });
forEach(categories, category => {
const selectedForCategory = find(plugins[category], { isDefault: true });
selected[category] = selectedForCategory.sourceName;
});

Expand Down Expand Up @@ -102,11 +103,11 @@ export function loadUserPlugin(path) {
try {
const api = createApi();
const pluginContents = await fs.promises.readFile(path, 'utf8');
const transformedPluginContents = await remote.app.g(pluginContents);
const transformedPluginContents = await remote.app.transformSource(pluginContents);

const plugin = eval(transformedPluginContents.code);

if (_.isNil(plugin)) {
if (isNil(plugin)) {
throw new Error('Invalid plugin file');
}

Expand All @@ -119,7 +120,7 @@ export function loadUserPlugin(path) {
plugin.onLoad
);

if (_.isNil(pluginStruct.name)) {
if (isNil(pluginStruct.name)) {
throw new Error('Unnamed plugins are not allowed');
}

Expand Down Expand Up @@ -160,7 +161,7 @@ export function serializePlugins(plugins) {
export function deserializePlugins() {
return dispatch => {
const plugins = store.get('plugins') || [];
_.forEach(plugins, plugin => {
forEach(plugins, plugin => {
dispatch(loadUserPlugin(plugin.path));
});
};
Expand Down
7 changes: 7 additions & 0 deletions packages/app/app/actions/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export const SearchActions = {
}
};
}),
trackSearchSuccess: createStandardAction(Search.TRACK_SEARCH_SUCCESS)<SearchResultsAlbum[]>(),
artistSearchSuccess: createStandardAction(Search.ARTIST_SEARCH_SUCCESS)<SearchResultsArtist[]>(),
artistInfoStart: createStandardAction(Search.ARTIST_INFO_SEARCH_START)<string>(),
artistInfoSuccess: createStandardAction(Search.ARTIST_INFO_SEARCH_SUCCESS).map((artistId: string, info: ArtistDetails) => {
Expand Down Expand Up @@ -147,6 +148,12 @@ export const albumSearch = (terms: string) => async (dispatch, getState: () => R
dispatch(SearchActions.albumSearchSuccess(results));
};

export const trackSearch = (terms: string) => async (dispatch, getState: () => RootState) => {
const selectedProvider = getSelectedMetaProvider(getState);
const results = await selectedProvider.searchForTracks(terms);
dispatch(SearchActions.trackSearchSuccess(results));
};

export const podcastSearch = (terms: string) => async (dispatch, getState: () => RootState) => {
const selectedProvider = getSelectedMetaProvider(getState);
const results = await selectedProvider.searchForPodcast(terms);
Expand Down
28 changes: 14 additions & 14 deletions packages/app/app/actions/toasts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { v4 } from 'uuid';
import _ from 'lodash';
import { get } from 'lodash';
import { createStandardAction } from 'typesafe-actions';
import { Notification } from '@nuclear/ui/lib/types';
import { Toast } from './actionTypes';
Expand All @@ -10,39 +10,39 @@ export const addNotification = createStandardAction(Toast.ADD_NOTIFICATION)<Noti

export const removeNotification = createStandardAction(Toast.REMOVE_NOTIFICATION)<string>();

export function notify(title: string, details: string, icon: Node | React.ReactElement<{
export function notify(title: string, details: string, icon?: Node | React.ReactElement<{
src: string;
}>, settings: Setting[] | { [key: string]: unknown }) {
}>, settings?: Setting[] | { [key: string]: unknown }) {
return generateNotification(title, details, icon, {}, settings);
}

export function error(title: string, details: string, icon: Node | React.ReactElement<{
export function error(title: string, details: string, icon?: Node | React.ReactElement<{
src: string;
}>, settings: Setting[] | { [key: string]: unknown }) {
}>, settings?: Setting[] | { [key: string]: unknown }) {
return generateNotification(title, details, icon, {error: true}, settings);
}

export function warning(title: string, details: string, icon: Node | React.ReactElement<{
export function warning(title: string, details: string, icon?: Node | React.ReactElement<{
src: string;
}>, settings: Setting[] | { [key: string]: unknown }) {
}>, settings?: Setting[] | { [key: string]: unknown }) {
return generateNotification(title, details, icon, {warning: true}, settings);
}

export function success(title: string, details: string, icon: Node | React.ReactElement<{
export function success(title: string, details: string, icon?: Node | React.ReactElement<{
src: string;
}>, settings: Setting[] | { [key: string]: unknown }) {
}>, settings?: Setting[] | { [key: string]: unknown }) {
return generateNotification(title, details, icon, {success: true}, settings);
}

export function info(title: string, details: string, icon: Node | React.ReactElement<{
export function info(title: string, details: string, icon?: Node | React.ReactElement<{
src: string;
}>, settings: Setting[] | { [key: string]: unknown }) {
}>, settings?: Setting[] | { [key: string]: unknown }) {
return generateNotification(title, details, icon, {info: true}, settings);
}

function generateNotification(title: string, details: string, icon: Node | React.ReactElement<{
function generateNotification(title: string, details: string, icon?: Node | React.ReactElement<{
src: string;
}>, type: {[type:string]: boolean}, settings: Setting[] | { [key: string]: unknown }) {
}>, type?: {[type:string]: boolean}, settings?: Setting[] | { [key: string]: unknown }) {
return dispatch => {
const id = v4();
dispatch(addNotification(Object.assign({}, {
Expand All @@ -53,7 +53,7 @@ function generateNotification(title: string, details: string, icon: Node | Reac
},
type)));

const timeout = _.get(settings, 'notificationTimeout') ?? 3;
const timeout = get(settings, 'notificationTimeout') ?? 3;
setTimeout(() => dispatch(removeNotification(id)), timeout * 1000);
};
}
File renamed without changes.
2 changes: 1 addition & 1 deletion packages/app/app/components/AlbumView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export const AlbumView: React.FC<AlbumViewProps> = ({
}
>
<Icon
name={isFavorite ? 'star' : 'star outline'}
name={isFavorite ? 'heart' : 'heart outline'}
size='big'
/>
</a>
Expand Down
78 changes: 78 additions & 0 deletions packages/app/app/components/ArtistView/ArtistHeader/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from 'react';

import ArtistTags from '../ArtistTags';
import styles from '../styles.scss';
import { ArtistDetailsState } from '../../../reducers/search';
import artPlaceholder from '../../../../resources/media/art_placeholder.png';
import { Icon } from 'semantic-ui-react';
import { get } from 'lodash';
import { useTranslation } from 'react-i18next';

type ArtistHeaderProps = {
isOnTour: boolean;
isFavorite: boolean;
artist: ArtistDetailsState;
removeFavoriteArtist: React.MouseEventHandler;
addFavoriteArtist: React.MouseEventHandler;
}

export const ArtistHeader: React.FC<ArtistHeaderProps> = ({
isOnTour,
isFavorite,
artist,
removeFavoriteArtist,
addFavoriteArtist
}) => {
const { t }= useTranslation('artist');
return <div className={styles.artist_header_overlay}>
<div className={styles.artist_header_container}>
{
artist.images &&
<div
className={styles.artist_avatar}
style={{
background: `url('${get(artist, 'images[1]', artPlaceholder)
}')`,
backgroundRepeat: 'noRepeat',
backgroundPosition: 'center',
backgroundSize: 'cover'
}}
/>
}

<div className={styles.artist_name_container}>
<div className={styles.artist_name_line}>
<h1>{artist.name}</h1>
{
isOnTour &&
<span
className={styles.on_tour}
>
{ t('tour') }
</span>
}

<a
href='#'
className={styles.artist_favorites_button_wrap}
data-testid='add-remove-favorite'
onClick={
isFavorite
? removeFavoriteArtist
: addFavoriteArtist
}
>
<Icon
name={isFavorite ? 'heart' : 'heart outline'}
size='big'
/>
</a>
</div>

<ArtistTags
tags={artist.tags}
/>
</div>
</div>
</div>;
};
Loading

0 comments on commit 315ad9b

Please sign in to comment.