Skip to content

Commit e978e77

Browse files
author
Meyn
committed
Clean up and fix various issues
- Clean up README for better clarity - Fix overly strict YouTube search criteria - Resolve FFmpeg execution issue - Rename to standard namespaces
1 parent 6d3a821 commit e978e77

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+465
-230
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
name: Bug Report
3+
about: Create a report to help us improve
4+
title: "[BUG]"
5+
labels: bug
6+
assignees:
7+
---
8+
9+
**Version**
10+
- Tubifarry Version: [e.g., v1.2.0]
11+
- Lidarr Version: [e.g., v1.0.0]
12+
- Operating System: [e.g., Windows 10, Ubuntu 20.04, Docker]
13+
14+
**Describe the Bug**
15+
A clear and concise description of what the bug is.
16+
17+
**Feature Problem**
18+
Explain the specific feature or functionality that is not working as intended.
19+
20+
**To Reproduce**
21+
Steps to reproduce the behavior:
22+
1. Go to '...'
23+
2. Click on '....'
24+
3. Scroll down to '....'
25+
4. See error
26+
27+
**Expected Behavior**
28+
A clear and concise description of what you expected to happen.
29+
30+
**Screenshots**
31+
<details>
32+
<summary>For Pictures click here</summary>
33+
34+
Add screenshots here to help explain your problem.
35+
</details>
36+
37+
**Additional Context**
38+
Add any other context about the problem here.

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# .github/ISSUE_TEMPLATE/config.yml
2+
3+
# Disable blank issues (users must use a template)
4+
blank_issues_enabled: false
5+
6+
# Set up contact links for additional support options
7+
contact_links:
8+
- name: 🐛 Bug Reports
9+
url: https://github.com/TypNull/Tubifarry/issues/new?template=bug_report.md
10+
about: Report a bug or unexpected behavior in Tubifarry.
11+
12+
- name: ✨ Feature Requests
13+
url: https://github.com/TypNull/Tubifarry/issues/new?template=feature_request.md
14+
about: Suggest a new feature or improvement for Tubifarry.
15+
16+
- name: ❓ Questions & Support
17+
url: https://github.com/TypNull/Tubifarry/discussions
18+
about: Ask questions or get help with Tubifarry in our Discussions forum.
19+
20+
- name: 📚 Documentation
21+
url: https://github.com/TypNull/Tubifarry/wiki
22+
about: Check out the official documentation for Tubifarry.
23+
24+
# Optional: Customize the order of templates (if you have multiple templates)
25+
# templates:
26+
# - feature_request.md
27+
# - bug_report.md
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
name: Feature Request
3+
about: Suggest an idea or improvement for Tubifarry
4+
title: "[FEATURE]"
5+
labels: enhancement
6+
assignees:
7+
---
8+
9+
**Is your feature request related to a problem? Please describe.**
10+
A clear and concise description of what the problem is. For example:
11+
- "I’m always frustrated when..."
12+
- "Currently, it’s difficult to..."
13+
14+
**Describe the Solution You'd Like**
15+
A clear and concise description of what you want to happen. Include any specific functionality or changes you’d like to see.
16+
17+
**Describe Alternatives You've Considered**
18+
A clear and concise description of any alternative solutions or features you've considered. This helps us understand your thought process and explore other options.
19+
20+
**Additional Context**
21+
Add any other context, screenshots, or examples about the feature request here. For example:
22+
- Why this feature would be valuable.
23+
- How it aligns with the goals of Tubifarry.
24+
- Any technical considerations or constraints.
25+
26+
<details>
27+
<summary>For Screenshots or Mockups, click here</summary>
28+
29+
Add any relevant screenshots, diagrams, or mockups to illustrate your idea.
30+
</details>
31+
32+
---
33+
34+
**Technical Details (Optional)**
35+
If you have technical expertise or ideas for implementation, feel free to share:
36+
- Proposed architecture or design.
37+
- APIs or libraries that could be used.
38+
- Any potential challenges or risks.
39+
40+
---
41+
42+
**Contributions Welcome!**
43+
If you’re interested in contributing to this feature, let us know! We’re happy to guide you through the process.
44+
45+
---
46+
47+
**Thank You!**
48+
We appreciate your feedback and ideas. Your input helps make Tubifarry better for everyone! 🎉

README.md

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@ Additionally, Tubifarry supports fetching soundtracks from **Sonarr** (series) a
99

1010
## Table of Contents 📑
1111

12-
1. [Tubifarry for Lidarr 🎶](#tubifarry-for-lidarr-)
13-
2. [Installation 🚀](#installation-)
14-
3. [Soulseek (Slskd) Setup 🎧](#soulseek-slskd-setup-)
15-
4. [YouTube Downloader Setup 🎥](#youtube-downloader-setup-)
16-
5. [Fetching Soundtracks 🎬🎵](#fetching-soundtracks-from-sonarr-and-radarr-)
17-
6. [Queue Cleaner 🧹](#queue-cleaner-)
18-
7. [Troubleshooting 🛠️](#troubleshooting-)
12+
1. [Installation 🚀](#installation-)
13+
2. [Soulseek (Slskd) Setup 🎧](#soulseek-slskd-setup-)
14+
3. [YouTube Downloader Setup 🎥](#youtube-downloader-setup-)
15+
4. [Fetching Soundtracks 🎬🎵](#fetching-soundtracks-from-sonarr-and-radarr-)
16+
5. [Queue Cleaner 🧹](#queue-cleaner-)
17+
6. [Troubleshooting 🛠️](#troubleshooting-%EF%B8%8F)
1918

2019
----
2120

@@ -64,6 +63,10 @@ Tubifarry supports **Slskd**, the Soulseek client, as both an **indexer** and **
6463
### YouTube Downloader Setup 🎥
6564
Tubifarry allows you to download music directly from YouTube. Follow the steps below to configure the YouTube downloader.
6665

66+
#### **Configure the Indexer**:
67+
1. Navigate to `Settings -> Indexers` and click **Add**.
68+
2. In the modal, select `Tubifarry` (located under **Other** at the bottom).
69+
6770
#### **Setting Up the YouTube Download Client**:
6871
1. Go to `Settings -> Download Clients` and click **Add**.
6972
2. Select `Youtube` from the list of download clients.
@@ -82,9 +85,6 @@ Tubifarry allows you to download music directly from YouTube. Follow the steps b
8285

8386
**Note**: For higher-quality audio (e.g., 256kb/s), you need a **YouTube Premium subscription**.
8487

85-
3. **Saving .lrc Files**:
86-
If you want to save **.lrc files** (lyric files), navigate to **Media Management > Advanced Settings > Import Extra Files** and add `lrc` to the list of supported file types. This ensures that lyric files are imported and saved alongside your music files.
87-
8888
---
8989

9090
### Fetching Soundtracks from Sonarr and Radarr 🎬🎵
@@ -120,18 +120,28 @@ The **Queue Cleaner** automatically processes items in your Lidarr queue that ha
120120
---
121121

122122
## Troubleshooting 🛠️
123-
- **Slskd Download Path Permissions**: Ensure Lidarr has read/write access to the Slskd download path. Verify folder permissions and ensure the user running Lidarr has the necessary access. For Docker setups, confirm the volume is correctly mounted and permissions are set.
124-
- **Optional: FFmpeg Issues**: If you choose to use FFmpeg and songs fail to process, verify that FFmpeg is correctly installed and accessible in your system's PATH. If not, try reinstalling or downloading it manually.
125-
- **Metadata Issues**: If metadata is not being added to downloaded files, confirm that the files are in a supported format. If using FFmpeg, ensure it is extracting audio to formats like AAC embedded in MP4 containers (check debug logs).
126-
- **No Release Found**: If no release is found, YouTube might flag the plugin as a bot (which it technically is). To avoid this and access higher-quality audio, you can log in using cookies.
127-
- **Steps to Use Cookies**:
128-
1. Install the **cookies.txt** extension for your browser:
129-
- [Get cookies.txt for Chrome](https://chrome.google.com/webstore/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc)
130-
- [Get cookies.txt for Firefox](https://addons.mozilla.org/en-US/firefox/addon/cookies-txt/)
131-
2. Log in to YouTube and save the cookies.txt file in a folder accessible by Lidarr.
132-
3. Go to the **Indexer and Downloader** settings in Lidarr and add the file path to the cookies.txt file.
133123

134-
---
124+
- **Slskd Download Path Permissions**:
125+
Ensure Lidarr has read/write access to the Slskd download path. Verify folder permissions and confirm the user running Lidarr has the necessary access. For Docker setups, double-check that the volume is correctly mounted and permissions are properly configured.
126+
127+
- **FFmpeg Issues (Optional)**:
128+
If you’re using FFmpeg and songs fail to process, ensure FFmpeg is installed correctly and accessible in your system’s PATH. If issues persist, try reinstalling FFmpeg or downloading it manually.
129+
130+
- **Metadata Issues**:
131+
If metadata isn’t being added to downloaded files, confirm the files are in a supported format. If using FFmpeg, check that it’s extracting audio to compatible formats like AAC embedded in MP4 containers. Review debug logs for further details.
132+
133+
- **No Release Found**:
134+
If no release is found, YouTube may flag the plugin as a bot. To avoid this and access higher-quality audio, log in using cookies:
135+
1. Install the **cookies.txt** extension for your browser:
136+
- [Chrome](https://chrome.google.com/webstore/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc)
137+
- [Firefox](https://addons.mozilla.org/en-US/firefox/addon/cookies-txt/)
138+
2. Log in to YouTube and save the `cookies.txt` file in a folder accessible by Lidarr.
139+
3. In Lidarr, go to **Indexer and Downloader Settings** and provide the path to the `cookies.txt` file.
140+
141+
- **No Lyrics Imported**:
142+
To save `.lrc` files (lyric files), navigate to **Media Management > Advanced Settings > Import Extra Files** and add `lrc` to the list of supported file types. This ensures lyric files are imported and saved alongside your music files.
143+
144+
---
135145

136146
## Acknowledgments 🙌
137147
Special thanks to [**trevTV**](https://github.com/TrevTV) for laying the groundwork with his plugins. Additionally, thanks to [**IcySnex**](https://github.com/IcySnex) for providing the YouTube API. 🎉

Tubifarry/Blocklisting/BaseBlocklist.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
using NzbDrone.Common.Extensions;
2+
using NzbDrone.Core.Blocklisting;
23
using NzbDrone.Core.Download;
34
using NzbDrone.Core.Indexers;
45
using NzbDrone.Core.Parser.Model;
56

6-
namespace NzbDrone.Core.Blocklisting
7+
namespace Tubifarry.Blocklisting
78
{
89
public abstract class BaseBlocklist<TProtocol> : IBlocklistForProtocol where TProtocol : IDownloadProtocol
910
{

Tubifarry/Blocklisting/Blocklists.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
using NzbDrone.Core.Blocklisting;
12
using NzbDrone.Core.Indexers;
23

3-
namespace NzbDrone.Core.Blocklisting
4+
namespace Tubifarry.Blocklisting
45
{
56
public class YoutubeBlocklist : BaseBlocklist<YoutubeDownloadProtocol>
67
{
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
using NzbDrone.Core.Indexers;
22
using NzbDrone.Core.Parser.Model;
33
using System.Text.RegularExpressions;
4+
using Tubifarry.Core.Utilities;
45

5-
namespace Tubifarry.Core
6+
namespace Tubifarry.Core.Model
67
{
78
/// <summary>
89
/// Contains combined information about an album, search parameters, and search results.
@@ -88,7 +89,7 @@ private string ConstructTitle()
8889

8990
int calculatedBitrate = Bitrate;
9091
if (calculatedBitrate <= 0 && Size.HasValue && Duration > 0)
91-
calculatedBitrate = (int)((Size.Value * 8) / (Duration * 1000));
92+
calculatedBitrate = (int)(Size.Value * 8 / (Duration * 1000));
9293

9394
if (AudioFormatHelper.IsLossyFormat(Codec) && calculatedBitrate != 0)
9495
title += $" [{Codec} {calculatedBitrate}kbps] [WEB]";
@@ -111,9 +112,9 @@ private static string NormalizeAlbumName(string albumName)
111112
if (featRegex.IsMatch(albumName))
112113
{
113114
Match match = featRegex.Match(albumName);
114-
string featuringArtist = albumName.Substring(match.Index + match.Length).Trim();
115+
string featuringArtist = albumName[(match.Index + match.Length)..].Trim();
115116

116-
albumName = $"{albumName.Substring(0, match.Index).Trim()} (feat. {featuringArtist})";
117+
albumName = $"{albumName[..match.Index].Trim()} (feat. {featuringArtist})";
117118
}
118119
albumName = Regex.Replace(albumName, @"\((?!feat\.)[^)]*\)", match => $"{{{match.Value.Trim('(', ')')}}}");
119120

Tubifarry/Core/AudioMetadataHandler.cs renamed to Tubifarry/Core/Model/AudioMetadataHandler.cs

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
using NLog;
22
using NzbDrone.Common.Instrumentation;
33
using NzbDrone.Core.Parser.Model;
4+
using Tubifarry.Core.Records;
5+
using Tubifarry.Core.Utilities;
46
using Xabe.FFmpeg;
57
using Xabe.FFmpeg.Downloader;
68
using YouTubeMusicAPI.Models.Info;
79

8-
namespace Tubifarry.Core
10+
namespace Tubifarry.Core.Model
911
{
1012
internal class AudioMetadataHandler
1113
{
1214
private readonly Logger? _logger;
15+
private static bool? _isFFmpegInstalled = null;
1316

1417
public string TrackPath { get; private set; }
1518
public Lyric? Lyric { get; set; }
@@ -23,7 +26,6 @@ public AudioMetadataHandler(string originalPath)
2326
_logger = NzbDroneLogger.GetLogger(this); ;
2427
}
2528

26-
2729
private static readonly Dictionary<AudioFormat, string[]> ConversionParameters = new()
2830
{
2931
{ AudioFormat.AAC, new[] { "-codec:a aac", "-q:a 0", "-movflags +faststart" } },
@@ -50,7 +52,7 @@ public AudioMetadataHandler(string originalPath)
5052

5153
public async Task<bool> TryConvertToFormatAsync(AudioFormat audioFormat)
5254
{
53-
if (!FFmpegIsInstalled)
55+
if (!CheckFFmpegInstalled())
5456
return false;
5557

5658
if (!await TryExtractAudioFromVideoAsync())
@@ -122,7 +124,7 @@ public async Task<bool> IsVideoContainerAsync()
122124

123125
public async Task<bool> TryExtractAudioFromVideoAsync()
124126
{
125-
if (!FFmpegIsInstalled)
127+
if (!CheckFFmpegInstalled())
126128
return false;
127129

128130
bool isVideo = await IsVideoContainerAsync();
@@ -271,12 +273,77 @@ public bool TryEmbedMetadata(AlbumInfo albumInfo, AlbumSongInfo trackInfo, Relea
271273
return true;
272274
}
273275

274-
public static bool FFmpegIsInstalled => !string.IsNullOrEmpty(FFmpeg.ExecutablesPath) && Directory.GetFiles(FFmpeg.ExecutablesPath, "ffmpeg.*").Any();
276+
277+
public static bool CheckFFmpegInstalled()
278+
{
279+
if (_isFFmpegInstalled.HasValue)
280+
return _isFFmpegInstalled.Value;
281+
282+
bool isInstalled = false;
283+
284+
if (!string.IsNullOrEmpty(FFmpeg.ExecutablesPath) && Directory.Exists(FFmpeg.ExecutablesPath))
285+
{
286+
string[] ffmpegPatterns = new[] { "ffmpeg", "ffmpeg.exe", "ffmpeg.bin" };
287+
string[] files = Directory.GetFiles(FFmpeg.ExecutablesPath);
288+
if (files.Any(file => ffmpegPatterns.Contains(Path.GetFileName(file), StringComparer.OrdinalIgnoreCase) && IsExecutable(file)))
289+
isInstalled = true;
290+
}
291+
292+
if (!isInstalled)
293+
{
294+
string[] paths = Environment.GetEnvironmentVariable("PATH")?.Split(Path.PathSeparator) ?? Array.Empty<string>();
295+
foreach (string path in paths)
296+
{
297+
if (Directory.Exists(path))
298+
{
299+
string[] ffmpegPatterns = new[] { "ffmpeg", "ffmpeg.exe", "ffmpeg.bin" };
300+
string[] files = Directory.GetFiles(path);
301+
302+
if (files.Any(file => ffmpegPatterns.Contains(Path.GetFileName(file), StringComparer.OrdinalIgnoreCase) && IsExecutable(file)))
303+
{
304+
isInstalled = true;
305+
break;
306+
}
307+
}
308+
}
309+
}
310+
311+
_isFFmpegInstalled = isInstalled;
312+
return isInstalled;
313+
}
314+
315+
316+
private static bool IsExecutable(string filePath)
317+
{
318+
try
319+
{
320+
using FileStream stream = File.OpenRead(filePath);
321+
byte[] magicNumber = new byte[4];
322+
stream.Read(magicNumber, 0, 4);
323+
324+
if (magicNumber[0] == 0x4D && magicNumber[1] == 0x5A)
325+
return true;
326+
if (magicNumber[0] == 0x7F && magicNumber[1] == 0x45 && magicNumber[2] == 0x4C && magicNumber[3] == 0x46)
327+
return true;
328+
if (magicNumber[0] == 0xCF && magicNumber[1] == 0xFA && magicNumber[2] == 0xED && magicNumber[3] == 0xFE)
329+
return true;
330+
if (magicNumber[0] == 0xCE && magicNumber[1] == 0xFA && magicNumber[2] == 0xED && magicNumber[3] == 0xFE)
331+
return true;
332+
}
333+
catch
334+
{
335+
}
336+
337+
return false;
338+
}
339+
340+
public static void ResetFFmpegInstallationCheck() => _isFFmpegInstalled = null;
275341

276342
public static Task InstallFFmpeg(string path)
277343
{
344+
ResetFFmpegInstallationCheck();
278345
FFmpeg.SetExecutablesPath(path);
279-
return FFmpegIsInstalled ? Task.CompletedTask : FFmpegDownloader.GetLatestVersion(FFmpegVersion.Official, path);
346+
return CheckFFmpegInstalled() ? Task.CompletedTask : FFmpegDownloader.GetLatestVersion(FFmpegVersion.Official, path);
280347
}
281348
}
282349
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System.Text.Json;
22

3-
namespace Tubifarry.Core
3+
namespace Tubifarry.Core.Model
44
{
55
public class FileCache
66
{

0 commit comments

Comments
 (0)