-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Meyn
committed
Jan 18, 2025
1 parent
8772cfa
commit 8e8faf6
Showing
8 changed files
with
474 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,8 @@ | ||
using FluentValidation; | ||
using NzbDrone.Core.Annotations; | ||
using NzbDrone.Core.Indexers; | ||
using NzbDrone.Core.Validation; | ||
using Tubifarry.Core.Utilities; | ||
using Tubifarry.Indexers.Youtube; | ||
|
||
namespace Tubifarry.Indexers.Spotify | ||
{ | ||
public class SpotifyIndexerSettingsValidator : AbstractValidator<SpotifyIndexerSettings> | ||
{ | ||
public SpotifyIndexerSettingsValidator() | ||
{ | ||
// Validate CookiePath (if provided) | ||
RuleFor(x => x.CookiePath) | ||
.Must(path => string.IsNullOrEmpty(path) || File.Exists(path)) | ||
.WithMessage("Cookie file does not exist. Please provide a valid path to the cookies file.") | ||
.Must(path => string.IsNullOrEmpty(path) || CookieManager.ParseCookieFile(path).Any()) | ||
.WithMessage("Cookie file is invalid or contains no valid cookies."); | ||
} | ||
} | ||
public class SpotifyIndexerSettingsValidator : YoutubeIndexerSettingsValidator { } | ||
|
||
public class SpotifyIndexerSettings : IIndexerSettings | ||
{ | ||
private static readonly SpotifyIndexerSettingsValidator Validator = new(); | ||
|
||
[FieldDefinition(0, Type = FieldType.Number, Label = "Early Download Limit", Unit = "days", HelpText = "Time before release date Lidarr will download from this indexer, empty is no limit", Advanced = true)] | ||
public int? EarlyReleaseLimit { get; set; } = null; | ||
|
||
[FieldDefinition(1, Label = "Cookie Path", Type = FieldType.FilePath, Placeholder = "/path/to/cookies.txt", HelpText = "Specify the path to the Spotify cookies file. This is optional but required for accessing restricted content.", Advanced = true)] | ||
public string CookiePath { get; set; } = string.Empty; | ||
|
||
public string BaseUrl { get; set; } = string.Empty; | ||
|
||
public NzbDroneValidationResult Validate() => new(Validator.Validate(this)); | ||
} | ||
public class SpotifyIndexerSettings : YoutubeIndexerSettings { } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
using Newtonsoft.Json.Linq; | ||
using YouTubeMusicAPI.Models; | ||
using YouTubeMusicAPI.Types; | ||
|
||
namespace Tubifarry.Indexers.Youtube | ||
{ | ||
internal static class YoutubeAPISelectors | ||
{ | ||
public static T SelectObject<T>(this JToken value, string path) | ||
{ | ||
object? result = value.SelectToken(path)?.ToObject(typeof(T)); | ||
return result == null ? throw new ArgumentNullException(path, "Required token is null.") : (T)result; | ||
} | ||
|
||
public static T? SelectObjectOptional<T>(this JToken value, string path) => | ||
(T?)value.SelectToken(path)?.ToObject(typeof(T)); | ||
|
||
public static Radio SelectRadio(this JToken value, string playlistIdPath = "menu.menuRenderer.items[0].menuNavigationItemRenderer.navigationEndpoint.watchEndpoint.playlistId", string? videoIdPath = null) => | ||
new(value.SelectObject<string>(playlistIdPath), videoIdPath == null ? null : value.SelectObjectOptional<string>(videoIdPath)); | ||
|
||
public static Thumbnail[] SelectThumbnails(this JToken value, string path = "thumbnail.musicThumbnailRenderer.thumbnail.thumbnails") | ||
{ | ||
JToken? thumbnails = value.SelectToken(path); | ||
if (thumbnails == null) return Array.Empty<Thumbnail>(); | ||
|
||
return thumbnails | ||
.Select(t => new | ||
{ | ||
Url = t.SelectToken("url")?.ToString(), | ||
Width = t.SelectToken("width")?.ToString(), | ||
Height = t.SelectToken("height")?.ToString() | ||
}) | ||
.Where(t => t.Url != null) | ||
.Select(t => new Thumbnail(t.Url!, int.Parse(t.Width ?? "0"), int.Parse(t.Height ?? "0"))) | ||
.ToArray(); | ||
} | ||
|
||
public static YouTubeMusicItem[] SelectArtists(this JToken value, string path, int startIndex = 0, int trimBy = 0) | ||
{ | ||
JToken[] runs = value.SelectObject<JToken[]>(path); | ||
return runs | ||
.Skip(startIndex) | ||
.Take(runs.Length - trimBy - startIndex) | ||
.Select(run => new | ||
{ | ||
Artist = run.SelectToken("text")?.ToString()?.Trim(), | ||
ArtistId = run.SelectToken("navigationEndpoint.browseEndpoint.browseId")?.ToString() | ||
}) | ||
.Where(a => a.Artist != null && a.Artist != "," && a.Artist != "&" && a.Artist != "•") | ||
.Select(a => new YouTubeMusicItem(a.Artist!, a.ArtistId, YouTubeMusicItemKind.Artists)) | ||
.ToArray(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
using FluentValidation.Results; | ||
using NLog; | ||
using NzbDrone.Common.Http; | ||
using NzbDrone.Core.Configuration; | ||
using NzbDrone.Core.Indexers; | ||
using NzbDrone.Core.Parser; | ||
using NzbDrone.Core.ThingiProvider; | ||
using Requests; | ||
using Tubifarry.Indexers.Spotify; | ||
|
||
namespace Tubifarry.Indexers.Youtube | ||
{ | ||
internal class YoutubeIndexer : HttpIndexerBase<SpotifyIndexerSettings> | ||
{ | ||
public override string Name => "Youtube"; | ||
public override string Protocol => nameof(YoutubeDownloadProtocol); | ||
public override bool SupportsRss => false; | ||
public override bool SupportsSearch => true; | ||
public override int PageSize => 50; | ||
public override TimeSpan RateLimit => new(30); | ||
|
||
private readonly IYoutubeRequestGenerator _indexerRequestGenerator; | ||
|
||
private readonly IYoutubeParser _parseIndexerResponse; | ||
|
||
public override ProviderMessage Message => new( | ||
"YouTube frequently blocks downloads to prevent unauthorized access. To confirm you're not a bot, you may need to provide additional verification. " + | ||
"This issue can often be partially resolved by using a `cookies.txt` file containing your login tokens. " + | ||
"Ensure the file is properly formatted and includes valid session data to bypass restrictions. " + | ||
"Note: YouTube does not always provide the best metadata for tracks, so you may need to manually verify or update track information.", | ||
ProviderMessageType.Warning | ||
); | ||
|
||
public YoutubeIndexer(IYoutubeParser parser, IYoutubeRequestGenerator generator, IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger) | ||
: base(httpClient, indexerStatusService, configService, parsingService, logger) | ||
{ | ||
_parseIndexerResponse = parser; | ||
_indexerRequestGenerator = generator; | ||
|
||
RequestHandler.MainRequestHandlers[0].MaxParallelism = 2; | ||
} | ||
|
||
protected override Task Test(List<ValidationFailure> failures) | ||
{ | ||
_parseIndexerResponse.SetCookies(Settings.CookiePath); | ||
_indexerRequestGenerator.SetCookies(Settings.CookiePath); | ||
return Task.CompletedTask; | ||
} | ||
|
||
public override IIndexerRequestGenerator GetRequestGenerator() => _indexerRequestGenerator; | ||
|
||
public override IParseIndexerResponse GetParser() => _parseIndexerResponse; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
| ||
using FluentValidation; | ||
using NzbDrone.Core.Annotations; | ||
using NzbDrone.Core.Indexers; | ||
using NzbDrone.Core.Validation; | ||
using Tubifarry.Core.Utilities; | ||
|
||
namespace Tubifarry.Indexers.Youtube | ||
{ | ||
|
||
public class YoutubeIndexerSettingsValidator : AbstractValidator<YoutubeIndexerSettings> | ||
{ | ||
public YoutubeIndexerSettingsValidator() | ||
{ | ||
// Validate CookiePath (if provided) | ||
RuleFor(x => x.CookiePath) | ||
.Must(path => string.IsNullOrEmpty(path) || File.Exists(path)) | ||
.WithMessage("Cookie file does not exist. Please provide a valid path to the cookies file.") | ||
.Must(path => string.IsNullOrEmpty(path) || CookieManager.ParseCookieFile(path).Any()) | ||
.WithMessage("Cookie file is invalid or contains no valid cookies."); | ||
} | ||
} | ||
|
||
public class YoutubeIndexerSettings : IIndexerSettings | ||
{ | ||
private static readonly YoutubeIndexerSettingsValidator Validator = new(); | ||
|
||
[FieldDefinition(0, Type = FieldType.Number, Label = "Early Download Limit", Unit = "days", HelpText = "Time before release date Lidarr will download from this indexer, empty is no limit", Advanced = true)] | ||
public int? EarlyReleaseLimit { get; set; } = null; | ||
|
||
[FieldDefinition(1, Label = "Cookie Path", Type = FieldType.FilePath, Placeholder = "/path/to/cookies.txt", HelpText = "Specify the path to the Spotify cookies file. This is optional but required for accessing restricted content.", Advanced = true)] | ||
public string CookiePath { get; set; } = string.Empty; | ||
|
||
public string BaseUrl { get; set; } = string.Empty; | ||
|
||
public NzbDroneValidationResult Validate() => new(Validator.Validate(this)); | ||
} | ||
} |
Oops, something went wrong.