update logging

This commit is contained in:
2025-07-04 08:46:41 +02:00
parent b0465a020d
commit 77e483d637
8 changed files with 96 additions and 87 deletions

View File

@ -1,15 +1,17 @@
import { log } from '$lib/log'; import { Logger } from '$lib/log';
import { TimelineReader } from '$lib/server/timeline'; import { TimelineReader } from '$lib/server/timeline';
import type { Handle, HandleServerError } from '@sveltejs/kit'; import type { Handle, HandleServerError } from '@sveltejs/kit';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
import fs from 'fs/promises'; import fs from 'fs/promises';
log.log('App startup'); const logger = new Logger('App');
logger.log('App startup');
TimelineReader.init(); TimelineReader.init();
export const handleError = (({ error }) => { export const handleError = (({ error }) => {
if (error instanceof Error) { if (error instanceof Error) {
log.error('Something went wrong: ', error.name, error.message); logger.error('Something went wrong: ', error.name, error.message);
} }
return { return {
@ -21,7 +23,7 @@ export const handle = (async ({ event, resolve }) => {
const searchParams = event.url.searchParams; const searchParams = event.url.searchParams;
const authCode = searchParams.get('code'); const authCode = searchParams.get('code');
if (authCode) { if (authCode) {
log.debug('received GET hook', event.url.searchParams); logger.debug('received GET hook', event.url.searchParams);
} }
// Reeder *insists* on checking /feed instead of /feed.xml // Reeder *insists* on checking /feed instead of /feed.xml
@ -45,7 +47,7 @@ export const handle = (async ({ event, resolve }) => {
const readStream = fd const readStream = fd
.readableWebStream() .readableWebStream()
.getReader({ mode: 'byob' }) as ReadableStream<Uint8Array>; .getReader({ mode: 'byob' }) as ReadableStream<Uint8Array>;
log.info('sending. size: ', stat.size); logger.info('sending. size: ', stat.size);
return new Response(readStream, { return new Response(readStream, {
headers: [ headers: [
['Content-Type', 'image/' + suffix], ['Content-Type', 'image/' + suffix],
@ -57,7 +59,7 @@ export const handle = (async ({ event, resolve }) => {
const f = await fs.readFile('avatars/' + fileName); const f = await fs.readFile('avatars/' + fileName);
return new Response(f, { headers: [['Content-Type', 'image/' + suffix]] }); return new Response(f, { headers: [['Content-Type', 'image/' + suffix]] });
} catch (e) { } catch (e) {
log.error('no stream', e); logger.error('no stream', e);
error(404); error(404);
} }
} }

View File

@ -4,6 +4,9 @@ const { DEV } = import.meta.env;
export const enableVerboseLog = isTruthy(env.VERBOSE); export const enableVerboseLog = isTruthy(env.VERBOSE);
/**
* @deprecated Use the new {@link Logger} class instead.
*/
export const log = { export const log = {
verbose: (...params: any[]) => { verbose: (...params: any[]) => {
if (!enableVerboseLog) { if (!enableVerboseLog) {

View File

@ -79,6 +79,7 @@ export class SpotifyPlaylistAdder extends OauthPlaylistAdder implements Playlist
return; return;
} }
// TODO. Spotify's check for "is this song already in the playlist" is... ugh
/* /*
const playlistItemsUrl = new URL(`${this.apiBase}/playlists/${SPOTIFY_PLAYLIST_ID}/tracks`); const playlistItemsUrl = new URL(`${this.apiBase}/playlists/${SPOTIFY_PLAYLIST_ID}/tracks`);
playlistItemsUrl.searchParams.append('videoId', youtubeId); playlistItemsUrl.searchParams.append('videoId', youtubeId);
@ -87,9 +88,9 @@ export class SpotifyPlaylistAdder extends OauthPlaylistAdder implements Playlist
/*const existingPlaylistItem = await fetch(this.apiBase + '/playlistItems', { /*const existingPlaylistItem = await fetch(this.apiBase + '/playlistItems', {
headers: { Authorization: `${token.token_type} ${token.access_token}` } headers: { Authorization: `${token.token_type} ${token.access_token}` }
}).then((r) => r.json()); }).then((r) => r.json());
log.debug('existingPlaylistItem', existingPlaylistItem); logger.debug('existingPlaylistItem', existingPlaylistItem);
if (existingPlaylistItem.pageInfo && existingPlaylistItem.pageInfo.totalResults > 0) { if (existingPlaylistItem.pageInfo && existingPlaylistItem.pageInfo.totalResults > 0) {
log.info('Item already in playlist'); logger.info('Item already in playlist');
return; return;
}*/ }*/

View File

@ -4,7 +4,7 @@ import {
YOUTUBE_CLIENT_SECRET, YOUTUBE_CLIENT_SECRET,
YOUTUBE_PLAYLIST_ID YOUTUBE_PLAYLIST_ID
} from '$env/static/private'; } from '$env/static/private';
import { log } from '$lib/log'; import { Logger } from '$lib/log';
import type { OauthResponse } from '$lib/mastodon/response'; import type { OauthResponse } from '$lib/mastodon/response';
import type { SongInfo } from '$lib/odesliResponse'; import type { SongInfo } from '$lib/odesliResponse';
import { OauthPlaylistAdder } from './oauthPlaylistAdder'; import { OauthPlaylistAdder } from './oauthPlaylistAdder';
@ -13,6 +13,7 @@ import type { PlaylistAdder } from './playlistAdder';
export class YoutubePlaylistAdder extends OauthPlaylistAdder implements PlaylistAdder { export class YoutubePlaylistAdder extends OauthPlaylistAdder implements PlaylistAdder {
public constructor() { public constructor() {
super('https://www.googleapis.com/youtube/v3', 'yt_auth_token'); super('https://www.googleapis.com/youtube/v3', 'yt_auth_token');
this.logger = new Logger('YoutubePlaylistAdder');
} }
public constructAuthUrl(redirectUri: URL): URL { public constructAuthUrl(redirectUri: URL): URL {
@ -31,7 +32,7 @@ export class YoutubePlaylistAdder extends OauthPlaylistAdder implements Playlist
} }
public async receivedAuthCode(code: string, url: URL) { public async receivedAuthCode(code: string, url: URL) {
log.debug('received code'); this.logger.debug('received code');
const tokenUrl = new URL('https://oauth2.googleapis.com/token'); const tokenUrl = new URL('https://oauth2.googleapis.com/token');
await this.receivedAuthCodeInternal( await this.receivedAuthCodeInternal(
tokenUrl, tokenUrl,
@ -52,7 +53,7 @@ export class YoutubePlaylistAdder extends OauthPlaylistAdder implements Playlist
return token; return token;
} }
if (!token.refresh_token) { if (!token.refresh_token) {
log.error('Need to refresh access token, but no refresh token provided'); this.logger.error('Need to refresh access token, but no refresh token provided');
return null; return null;
} }
@ -67,31 +68,31 @@ export class YoutubePlaylistAdder extends OauthPlaylistAdder implements Playlist
} }
public async addToPlaylist(song: SongInfo) { public async addToPlaylist(song: SongInfo) {
log.debug('addToYoutubePlaylist'); this.logger.debug('addToYoutubePlaylist');
const token = await this.refreshToken(); const token = await this.refreshToken();
if (token == null) { if (token == null) {
return; return;
} }
if (!YOUTUBE_PLAYLIST_ID || YOUTUBE_PLAYLIST_ID === 'CHANGE_ME') { if (!YOUTUBE_PLAYLIST_ID || YOUTUBE_PLAYLIST_ID === 'CHANGE_ME') {
log.debug('no playlist ID configured'); this.logger.debug('no playlist ID configured');
return; return;
} }
if (!song.youtubeUrl) { if (!song.youtubeUrl) {
log.info('Skip adding song to YT playlist, no youtube Url', song); this.logger.info('Skip adding song to YT playlist, no youtube Url', song);
return; return;
} }
const songUrl = new URL(song.youtubeUrl); const songUrl = new URL(song.youtubeUrl);
const youtubeId = songUrl.searchParams.get('v'); const youtubeId = songUrl.searchParams.get('v');
if (!youtubeId) { if (!youtubeId) {
log.debug( this.logger.debug(
'Skip adding song to YT playlist, could not extract YT id from URL', 'Skip adding song to YT playlist, could not extract YT id from URL',
song.youtubeUrl song.youtubeUrl
); );
return; return;
} }
log.debug('Found YT id from URL', song.youtubeUrl, youtubeId); this.logger.debug('Found YT id from URL', song.youtubeUrl, youtubeId);
const playlistItemsUrl = new URL(this.apiBase + '/playlistItems'); const playlistItemsUrl = new URL(this.apiBase + '/playlistItems');
playlistItemsUrl.searchParams.append('videoId', youtubeId); playlistItemsUrl.searchParams.append('videoId', youtubeId);
@ -101,7 +102,7 @@ export class YoutubePlaylistAdder extends OauthPlaylistAdder implements Playlist
headers: { Authorization: `${token.token_type} ${token.access_token}` } headers: { Authorization: `${token.token_type} ${token.access_token}` }
}).then((r) => r.json()); }).then((r) => r.json());
if (existingPlaylistItem.pageInfo && existingPlaylistItem.pageInfo.totalResults > 0) { if (existingPlaylistItem.pageInfo && existingPlaylistItem.pageInfo.totalResults > 0) {
log.info('Item already in playlist', existingPlaylistItem); this.logger.info('Item already in playlist', existingPlaylistItem);
return; return;
} }
@ -122,9 +123,9 @@ export class YoutubePlaylistAdder extends OauthPlaylistAdder implements Playlist
}; };
const resp = await fetch(addItemUrl, options); const resp = await fetch(addItemUrl, options);
const respObj = await resp.json(); const respObj = await resp.json();
log.info('Added to playlist', youtubeId, song.title); this.logger.info('Added to playlist', youtubeId, song.title);
if (respObj.error) { if (respObj.error) {
log.debug('Add to playlist failed', respObj.error.errors); this.logger.debug('Add to playlist failed', respObj.error.errors);
} }
} }
} }

View File

@ -1,10 +1,12 @@
import { BASE_URL, WEBSUB_HUB } from '$env/static/private'; import { BASE_URL, WEBSUB_HUB } from '$env/static/private';
import { PUBLIC_MASTODON_INSTANCE_DISPLAY_NAME } from '$env/static/public'; import { PUBLIC_MASTODON_INSTANCE_DISPLAY_NAME } from '$env/static/public';
import type { Post } from '$lib//mastodon/response'; import type { Post } from '$lib//mastodon/response';
import { log } from '$lib/log'; import { Logger } from '$lib/log';
import { Feed } from 'feed'; import { Feed } from 'feed';
import fs from 'fs/promises'; import fs from 'fs/promises';
const logger = new Logger('RSS');
export function createFeed(posts: Post[]): Feed { export function createFeed(posts: Post[]): Feed {
const baseUrl = BASE_URL.endsWith('/') ? BASE_URL : BASE_URL + '/'; const baseUrl = BASE_URL.endsWith('/') ? BASE_URL : BASE_URL + '/';
const hub = WEBSUB_HUB ? WEBSUB_HUB : undefined; const hub = WEBSUB_HUB ? WEBSUB_HUB : undefined;
@ -60,6 +62,6 @@ export async function saveAtomFeed(feed: Feed) {
body: params body: params
}); });
} catch (e) { } catch (e) {
log.error('Failed to update WebSub hub', e); logger.error('Failed to update WebSub hub', e);
} }
} }

View File

@ -5,7 +5,7 @@ import {
ODESLI_API_KEY, ODESLI_API_KEY,
YOUTUBE_API_KEY YOUTUBE_API_KEY
} from '$env/static/private'; } from '$env/static/private';
import { log, Logger } from '$lib/log'; import { Logger } from '$lib/log';
import type { import type {
Account, Account,
AccountAvatar, AccountAvatar,
@ -47,11 +47,12 @@ export class TimelineReader {
private static _instance: TimelineReader; private static _instance: TimelineReader;
private lastPosts: string[] = []; private lastPosts: string[] = [];
private playlistAdders: PlaylistAdder[]; private playlistAdders: PlaylistAdder[];
private logger: Logger;
private static async isMusicVideo(videoId: string) { private async isMusicVideo(videoId: string) {
if (!YOUTUBE_API_KEY || YOUTUBE_API_KEY === 'CHANGE_ME') { if (!YOUTUBE_API_KEY || YOUTUBE_API_KEY === 'CHANGE_ME') {
// Assume that it *is* a music link when no YT API key is provided // Assume that it *is* a music link when no YT API key is provided
log.debug('YT API not configured'); this.logger.debug('YT API not configured');
return true; return true;
} }
const searchParams = new URLSearchParams([ const searchParams = new URLSearchParams([
@ -63,13 +64,13 @@ export class TimelineReader {
const resp = await fetch(youtubeVideoUrl); const resp = await fetch(youtubeVideoUrl);
const respObj = await resp.json(); const respObj = await resp.json();
if (!respObj.items.length) { if (!respObj.items.length) {
log.warn('Could not find video with id', videoId); this.logger.warn('Could not find video with id', videoId);
return false; return false;
} }
const item = respObj.items[0]; const item = respObj.items[0];
if (!item.snippet) { if (!item.snippet) {
log.warn('Could not load snippet for video', videoId, item); this.logger.warn('Could not load snippet for video', videoId, item);
return false; return false;
} }
if (item.snippet.tags?.includes('music')) { if (item.snippet.tags?.includes('music')) {
@ -87,16 +88,19 @@ export class TimelineReader {
const categoryTitle: string = await fetch(youtubeCategoryUrl) const categoryTitle: string = await fetch(youtubeCategoryUrl)
.then((r) => r.json()) .then((r) => r.json())
.then((r) => r.items[0]?.snippet?.title); .then((r) => r.items[0]?.snippet?.title);
log.debug('YT category', categoryTitle); this.logger.debug('YT category', categoryTitle);
return categoryTitle === 'Music'; return categoryTitle === 'Music';
} }
public static async getSongInfoInPost(post: Post): Promise<SongInfo[]> { public async getSongInfoInPost(post: Post): Promise<SongInfo[]> {
const urlMatches = post.content.matchAll(URL_REGEX); const urlMatches = post.content.matchAll(URL_REGEX);
const songs: SongInfo[] = []; const songs: SongInfo[] = [];
for (const match of urlMatches) { for (const match of urlMatches) {
if (match === undefined || match.groups === undefined) { if (match === undefined || match.groups === undefined) {
log.warn('Match listed in allMatches, but either it or its groups are undefined', match); this.logger.warn(
'Match listed in allMatches, but either it or its groups are undefined',
match
);
continue; continue;
} }
const urlMatch = match.groups.postUrl.toString(); const urlMatch = match.groups.postUrl.toString();
@ -104,14 +108,14 @@ export class TimelineReader {
try { try {
url = new URL(urlMatch); url = new URL(urlMatch);
} catch (e) { } catch (e) {
log.error('URL found via Regex does not seem to be a valud url', urlMatch, e); this.logger.error('URL found via Regex does not seem to be a valud url', urlMatch, e);
continue; continue;
} }
// Check *all* found url and let odesli determine if it is music or not // Check *all* found url and let odesli determine if it is music or not
log.debug(`Checking ${url} if it contains song data`); this.logger.debug(`Checking ${url} if it contains song data`);
const info = await TimelineReader.getSongInfo(url); const info = await this.getSongInfo(url);
//log.debug(`Found song info for ${url}?`, info); //this.logger.debug(`Found song info for ${url}?`, info);
if (info) { if (info) {
songs.push(info); songs.push(info);
} }
@ -119,9 +123,9 @@ export class TimelineReader {
return songs; return songs;
} }
private static async getSongInfo(url: URL, remainingTries = 6): Promise<SongInfo | null> { private async getSongInfo(url: URL, remainingTries = 6): Promise<SongInfo | null> {
if (remainingTries === 0) { if (remainingTries === 0) {
log.error('No tries remaining. Lookup failed!'); this.logger.error('No tries remaining. Lookup failed!');
return null; return null;
} }
if (url.hostname === 'songwhip.com') { if (url.hostname === 'songwhip.com') {
@ -153,7 +157,7 @@ export class TimelineReader {
return null; return null;
} }
const info = odesliInfo.entitiesByUniqueId[odesliInfo.entityUniqueId]; const info = odesliInfo.entitiesByUniqueId[odesliInfo.entityUniqueId];
//log.debug('odesli response', info); //this.logger.debug('odesli response', info);
const platform: Platform = 'youtube'; const platform: Platform = 'youtube';
if (info.platforms.includes(platform)) { if (info.platforms.includes(platform)) {
const youtubeId = const youtubeId =
@ -161,12 +165,16 @@ export class TimelineReader {
YOUTUBE_REGEX.exec(url.href)?.groups?.videoId ?? YOUTUBE_REGEX.exec(url.href)?.groups?.videoId ??
new URL(odesliInfo.pageUrl).pathname.split('/y/').pop(); new URL(odesliInfo.pageUrl).pathname.split('/y/').pop();
if (youtubeId === undefined) { if (youtubeId === undefined) {
log.warn('Looks like a youtube video, but could not extract a video id', url, odesliInfo); this.logger.warn(
'Looks like a youtube video, but could not extract a video id',
url,
odesliInfo
);
return null; return null;
} }
const isMusic = await TimelineReader.isMusicVideo(youtubeId); const isMusic = await this.isMusicVideo(youtubeId);
if (!isMusic) { if (!isMusic) {
log.debug('Probably not a music video', youtubeId, url); this.logger.debug('Probably not a music video', youtubeId, url);
return null; return null;
} }
} }
@ -181,11 +189,11 @@ export class TimelineReader {
} as SongInfo; } as SongInfo;
} catch (e) { } catch (e) {
if (e instanceof Error && e.cause === 429) { if (e instanceof Error && e.cause === 429) {
log.warn('song.link rate limit reached. Trying again in 10 seconds'); this.logger.warn('song.link rate limit reached. Trying again in 10 seconds');
await sleep(10_000); await sleep(10_000);
return await this.getSongInfo(url, remainingTries - 1); return await this.getSongInfo(url, remainingTries - 1);
} }
log.error(`Failed to load ${url} info from song.link`, e); this.logger.error(`Failed to load ${url} info from song.link`, e);
return null; return null;
} }
} }
@ -196,7 +204,7 @@ export class TimelineReader {
} }
} }
private static async resizeAvatar( private async resizeAvatar(
baseName: string, baseName: string,
size: number, size: number,
suffix: string, suffix: string,
@ -209,15 +217,15 @@ export class TimelineReader {
.then(() => true) .then(() => true)
.catch(() => false); .catch(() => false);
if (exists) { if (exists) {
log.debug('File already exists', fileName); this.logger.debug('File already exists', fileName);
return null; return null;
} }
log.debug('Saving avatar', fileName); this.logger.debug('Saving avatar', fileName);
await sharpAvatar.resize(size).toFile(fileName); await sharpAvatar.resize(size).toFile(fileName);
return fileName; return fileName;
} }
private static resizeAvatarPromiseMaker( private resizeAvatarPromiseMaker(
avatarFilenameBase: string, avatarFilenameBase: string,
baseSize: number, baseSize: number,
maxPixelDensity: number, maxPixelDensity: number,
@ -230,13 +238,7 @@ export class TimelineReader {
for (let i = 1; i <= maxPixelDensity; i++) { for (let i = 1; i <= maxPixelDensity; i++) {
promises.push( promises.push(
...formats.map((f) => ...formats.map((f) =>
TimelineReader.resizeAvatar( this.resizeAvatar(avatarFilenameBase, baseSize * i, `${i}x.${f}`, 'avatars', sharpAvatar)
avatarFilenameBase,
baseSize * i,
`${i}x.${f}`,
'avatars',
sharpAvatar
)
.then( .then(
(fn) => (fn) =>
({ ({
@ -252,7 +254,7 @@ export class TimelineReader {
return promises; return promises;
} }
private static resizeThumbnailPromiseMaker( private resizeThumbnailPromiseMaker(
filenameBase: string, filenameBase: string,
baseSize: number, baseSize: number,
maxPixelDensity: number, maxPixelDensity: number,
@ -266,13 +268,7 @@ export class TimelineReader {
for (let i = 1; i <= maxPixelDensity; i++) { for (let i = 1; i <= maxPixelDensity; i++) {
promises.push( promises.push(
...formats.map((f) => ...formats.map((f) =>
TimelineReader.resizeAvatar( this.resizeAvatar(filenameBase, baseSize * i, `${i}x.${f}`, 'thumbnails', sharpAvatar)
filenameBase,
baseSize * i,
`${i}x.${f}`,
'thumbnails',
sharpAvatar
)
.then( .then(
(fn) => (fn) =>
({ ({
@ -289,7 +285,7 @@ export class TimelineReader {
return promises; return promises;
} }
private static async saveAvatar(account: Account) { private async saveAvatar(account: Account) {
try { try {
const existingAvatars = await getAvatars(account.url, 1); const existingAvatars = await getAvatars(account.url, 1);
const existingAvatarBase = existingAvatars.shift()?.file.split('/').pop()?.split('_').shift(); const existingAvatarBase = existingAvatars.shift()?.file.split('/').pop()?.split('_').shift();
@ -302,7 +298,7 @@ export class TimelineReader {
const avatarsToDelete = (await fs.readdir('avatars')) const avatarsToDelete = (await fs.readdir('avatars'))
.filter((x) => x.startsWith(existingAvatarBase + '_')) .filter((x) => x.startsWith(existingAvatarBase + '_'))
.map((x) => { .map((x) => {
log.debug('Removing existing avatar file', x); this.logger.debug('Removing existing avatar file', x);
return x; return x;
}) })
.map((x) => fs.unlink('avatars/' + x)); .map((x) => fs.unlink('avatars/' + x));
@ -311,7 +307,7 @@ export class TimelineReader {
const avatarResponse = await fetch(account.avatar); const avatarResponse = await fetch(account.avatar);
const avatar = await avatarResponse.arrayBuffer(); const avatar = await avatarResponse.arrayBuffer();
await Promise.all( await Promise.all(
TimelineReader.resizeAvatarPromiseMaker( this.resizeAvatarPromiseMaker(
avatarFilenameBase, avatarFilenameBase,
50, 50,
3, 3,
@ -325,7 +321,7 @@ export class TimelineReader {
} }
} }
private static async saveSongThumbnails(songs: SongInfo[]) { private async saveSongThumbnails(songs: SongInfo[]) {
for (const song of songs) { for (const song of songs) {
if (!song.thumbnailUrl) { if (!song.thumbnailUrl) {
continue; continue;
@ -339,7 +335,7 @@ export class TimelineReader {
const imageResponse = await fetch(song.thumbnailUrl); const imageResponse = await fetch(song.thumbnailUrl);
const avatar = await imageResponse.arrayBuffer(); const avatar = await imageResponse.arrayBuffer();
await Promise.all( await Promise.all(
TimelineReader.resizeThumbnailPromiseMaker( this.resizeThumbnailPromiseMaker(
fileBaseName + '_large', fileBaseName + '_large',
200, 200,
3, 3,
@ -350,7 +346,7 @@ export class TimelineReader {
) )
); );
await Promise.all( await Promise.all(
TimelineReader.resizeThumbnailPromiseMaker( this.resizeThumbnailPromiseMaker(
fileBaseName + '_small', fileBaseName + '_small',
60, 60,
3, 3,
@ -375,27 +371,27 @@ export class TimelineReader {
const hashttags: string[] = HASHTAG_FILTER.split(','); const hashttags: string[] = HASHTAG_FILTER.split(',');
const found_tags: Tag[] = post.tags.filter((t: Tag) => hashttags.includes(t.name)); const found_tags: Tag[] = post.tags.filter((t: Tag) => hashttags.includes(t.name));
const songs = await TimelineReader.getSongInfoInPost(post); const songs = await this.getSongInfoInPost(post);
// If we don't have any tags or non-youtube urls, check youtube // If we don't have any tags or non-youtube urls, check youtube
// YT is handled separately, because it requires an API call and therefore is slower // YT is handled separately, because it requires an API call and therefore is slower
if (songs.length === 0 && found_tags.length === 0) { if (songs.length === 0 && found_tags.length === 0) {
log.log('Ignoring post', post.url); this.logger.log('Ignoring post', post.url);
return; return;
} }
await savePost(post, songs); await savePost(post, songs);
await TimelineReader.saveAvatar(post.account); await this.saveAvatar(post.account);
await TimelineReader.saveSongThumbnails(songs); await this.saveSongThumbnails(songs);
log.debug('Saved post', post.url, 'songs', songs); this.logger.debug('Saved post', post.url, 'songs', songs);
const posts = await getPosts(null, null, 100); const posts = await getPosts(null, null, 100);
await saveAtomFeed(createFeed(posts)); await saveAtomFeed(createFeed(posts));
for (let song of songs) { for (let song of songs) {
log.debug('Adding to playlist', song); this.logger.debug('Adding to playlist', song);
await this.addToPlaylist(song); await this.addToPlaylist(song);
} }
} }
@ -455,9 +451,9 @@ export class TimelineReader {
const now = new Date().toISOString(); const now = new Date().toISOString();
let latestPost = await getPosts(null, now, 1); let latestPost = await getPosts(null, now, 1);
if (latestPost.length > 0) { if (latestPost.length > 0) {
log.log('Last post in DB since', now, latestPost[0].created_at); this.logger.log('Last post in DB since', now, latestPost[0].created_at);
} else { } else {
log.log('No posts in DB since'); this.logger.log('No posts in DB since');
} }
let u = new URL(`https://${MASTODON_INSTANCE}/api/v1/timelines/public?local=true&limit=40`); let u = new URL(`https://${MASTODON_INSTANCE}/api/v1/timelines/public?local=true&limit=40`);
if (latestPost.length > 0) { if (latestPost.length > 0) {
@ -470,28 +466,28 @@ export class TimelineReader {
Authorization: `Bearer ${MASTODON_ACCESS_TOKEN}` Authorization: `Bearer ${MASTODON_ACCESS_TOKEN}`
}; };
const latestPosts: Post[] = await fetch(u, { headers }).then((r) => r.json()); const latestPosts: Post[] = await fetch(u, { headers }).then((r) => r.json());
log.info('searched posts', latestPosts.length); this.logger.info('searched posts', latestPosts.length);
for (const post of latestPosts) { for (const post of latestPosts) {
await this.checkAndSavePost(post); await this.checkAndSavePost(post);
} }
} }
private constructor() { private constructor() {
log.log('Constructing timeline object'); this.logger = new Logger('Timeline');
this.logger.log('Constructing timeline object');
this.playlistAdders = [new YoutubePlaylistAdder(), new SpotifyPlaylistAdder()]; this.playlistAdders = [new YoutubePlaylistAdder(), new SpotifyPlaylistAdder()];
this.startWebsocket(); this.startWebsocket();
this.loadPostsSinceLastRun() this.loadPostsSinceLastRun()
.then((_) => { .then((_) => {
log.info('loaded posts since last run'); this.logger.info('loaded posts since last run');
}) })
.catch((e) => { .catch((e) => {
log.error('cannot fetch latest posts', e); this.logger.error('cannot fetch latest posts', e);
}); });
} }
public static init() { public static init() {
log.log('Timeline object init');
if (this._instance === undefined) { if (this._instance === undefined) {
this._instance = new TimelineReader(); this._instance = new TimelineReader();
} }

View File

@ -1,20 +1,22 @@
import { log } from '$lib/log'; import { Logger } from '$lib/log';
import { SpotifyPlaylistAdder } from '$lib/server/playlist/spotifyPlaylistAdder'; import { SpotifyPlaylistAdder } from '$lib/server/playlist/spotifyPlaylistAdder';
import { redirect } from '@sveltejs/kit'; import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
const logger = new Logger('SpotifyAuth');
export const load: PageServerLoad = async ({ url }) => { export const load: PageServerLoad = async ({ url }) => {
const adder = new SpotifyPlaylistAdder(); const adder = new SpotifyPlaylistAdder();
let redirectUri = url; let redirectUri = url;
if (url.hostname === 'localhost') { if (url.hostname === 'localhost') {
redirectUri.hostname = '127.0.0.1'; redirectUri.hostname = '127.0.0.1';
} }
log.debug(url.searchParams, url.hostname); logger.debug(url.searchParams, url.hostname);
if (url.searchParams.has('code')) { if (url.searchParams.has('code')) {
await adder.receivedAuthCode(url.searchParams.get('code') || '', url); await adder.receivedAuthCode(url.searchParams.get('code') || '', url);
redirect(307, '/'); redirect(307, '/');
} else if (url.searchParams.has('error')) { } else if (url.searchParams.has('error')) {
log.error('received error', url.searchParams.get('error')); logger.error('received error', url.searchParams.get('error'));
return; return;
} }
@ -23,6 +25,6 @@ export const load: PageServerLoad = async ({ url }) => {
} }
const authUrl = adder.constructAuthUrl(url); const authUrl = adder.constructAuthUrl(url);
log.debug('+page.server.ts', authUrl.toString()); logger.debug('+page.server.ts', authUrl.toString());
redirect(307, authUrl); redirect(307, authUrl);
}; };

View File

@ -1,16 +1,18 @@
import { log } from '$lib/log'; import { Logger } from '$lib/log';
import { YoutubePlaylistAdder } from '$lib/server/playlist/ytPlaylistAdder'; import { YoutubePlaylistAdder } from '$lib/server/playlist/ytPlaylistAdder';
import { redirect } from '@sveltejs/kit'; import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
const logger = new Logger('YT Auth');
export const load: PageServerLoad = async ({ url }) => { export const load: PageServerLoad = async ({ url }) => {
const adder = new YoutubePlaylistAdder(); const adder = new YoutubePlaylistAdder();
if (url.searchParams.has('code')) { if (url.searchParams.has('code')) {
log.debug(url.searchParams); logger.debug(url.searchParams);
await adder.receivedAuthCode(url.searchParams.get('code') || '', url); await adder.receivedAuthCode(url.searchParams.get('code') || '', url);
redirect(307, '/'); redirect(307, '/');
} else if (url.searchParams.has('error')) { } else if (url.searchParams.has('error')) {
log.error('received error', url.searchParams.get('error')); logger.error('received error', url.searchParams.get('error'));
return; return;
} }
@ -19,6 +21,6 @@ export const load: PageServerLoad = async ({ url }) => {
} }
const authUrl = adder.constructAuthUrl(url); const authUrl = adder.constructAuthUrl(url);
log.debug('+page.server.ts', authUrl.toString()); logger.debug('+page.server.ts', authUrl.toString());
redirect(307, authUrl); redirect(307, authUrl);
}; };