Files
Musify/backend/Hosts/Musify.Api/AppSettings.json
T
Admin 56e06ccd5f Add a config toggle to enable/disable the YouTube Music source
- YouTubeConfiguration.Enabled (default true, Infrastructure): single
  switch for the whole source
- DisabledYouTubeMusicService: null-object IYouTubeMusicService that
  fails every call with Forbidden, registered instead of the real one
  when disabled (AddYouTubeMusicService) so no client is ever built
- AppSettings.json (Api + Worker): document YouTube.Enabled explicitly;
  overridable via YouTube__Enabled env var like every other section
- CurrentUser.cs: small unrelated cleanup, collapse the IsAuthenticated
  computation to a single null-coalescing expression

Search, albums, stream resolution and adding a new YouTube track all
surface as 403 Forbidden through the existing ErrorOr -> HTTP mapping.
Daily mix generation already treated a YouTube search failure as "no
YouTube candidates" and keeps working with local-only candidates —
added a test locking that in. Already-downloaded YouTube tracks are
unaffected since playback never goes through IYouTubeMusicService.

257/257 tests passing (7 new), 0 build warnings.
2026-09-09 23:00:15 +02:00

141 lines
4.1 KiB
JSON

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Authentication": {
"MetadataAddress": "",
"IssuerAddress": "",
"AudienceAddress": "",
"ClientId": "",
"AuthorizationEndpoint": "",
"TokenEndpoint": "",
"Scopes": [],
"ScalarRedirectUri": "",
"RequireHttpsMetadata": true
},
"Database": {
"ConnectionString": ""
},
"MassTransit": {
"Address": "",
"Username": "",
"Password": ""
},
"InfrastructureStorage": {
"Address": "",
"AccessKey": "",
"SecretAccessKey": "",
"ForcePathStyle": false,
"UseHttp": false
},
"ApplicationStorage": {
"Bucket": ""
},
"StreamGateway": {
"PublicBaseUrl": "",
"AudioFileName": "audio.m4a"
},
"StreamTicket": {
"PrivateKeyPath": "",
"Audience": "media-gateway",
"Issuer": "musify-webapi",
"TicketTtlSeconds": 3600
},
"OpenTelemetry": {
"OtlpEndpoint": ""
},
"YouTube": {
"Enabled": true,
"GeographicalLocation": "US",
"StreamUrlCacheSeconds": 1800,
"SearchContinuationCacheSeconds": 600,
"SearchResultsCacheSeconds": 900,
"SongInfoCacheSeconds": 86400,
"ThumbnailSize": 240,
"ArtworkSize": 544
},
"Mix": {
"ArtistMixCount": 3,
"ItemsPerMix": 12,
"SeedArtistCount": 3,
"YouTubeResultsPerSeed": 12
},
"PlayList": {
"Routes": {
"UploadsFolder": "uploads",
"ParentFolders": "PlayLists",
"OriginalPicturesFolder": "OriginalPictures",
"SmallPicturesFolder": "SmallPictures",
"MediumPicturesFolder": "MediumPictures",
"LargePicturesFolder": "LargePictures"
},
"PicturesSizes": {
"SmallPictureWidth": 128,
"SmallPictureHeight": 128,
"MediumPictureWidth": 256,
"MediumPictureHeight": 256,
"LargePictureWidth": 512,
"LargePictureHeight": 512
}
},
"Track": {
"Routes": {
"UploadsFolder": "uploads",
"ParentFolder": "Tracks",
"OriginalPicturesFolder": "OriginalPictures",
"SmallPicturesFolder": "SmallPictures",
"MediumPicturesFolder": "MediumPictures",
"LargePicturesFolder": "LargePictures",
"PresetOriginalPicture": "PresetOriginalPicture.webp",
"PresetSmallPicture": "PresetSmallPicture.webp",
"PresetMediumPicture": "PresetMediumPicture.webp",
"PresetLargePicture": "PresetLargePicture.webp",
"OriginalAudiosFolder": "OriginalAudios",
"ProcessedAudioFolder": "ProcessedAudios"
},
"PicturesSizes": {
"SmallPictureWidth": 128,
"SmallPictureHeight": 128,
"MediumPictureWidth": 256,
"MediumPictureHeight": 256,
"LargePictureWidth": 512,
"LargePictureHeight": 512
}
},
"AudioTranscoder": {
"TranscodingTimeout": "00:05:00",
"Ffmpeg": {
"ExecutableName": "ffmpeg",
"OutputFileName": "audio.m4a",
"AudioCodec": "aac",
"AudioBitrate": "128k",
"AudioChannels": 2,
"AudioSampleRate": 48000,
"AudioProfile": "aac_low",
"AdditionalArguments": ""
}
},
"UploadIntent": {
"UploadUrlExpiresInSeconds": 120,
"MaxActiveUploadIntentsPerUser": 5,
"MaxActiveUploadBytesPerUser": 209715200,
"MaxUploadBytes": 104857600,
"DefaultExpectedPictureSizeBytes": 10485760,
"DefaultExpectedAudioSizeBytes": 104857600,
"ExpirationJobIntervalSeconds": 300,
"ExpiredIntentsRetentionDays": 7,
"TempRootPrefix": "temp",
"TempUploadsRetentionDays": 3,
"TempCleanupJobIntervalSeconds": 3600
},
"Workers": {
"Routes": {
"TemporaryFilesDirectory": ""
}
}
}