aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/Library/PathExtensions.cs
blob: 1fc5526ae121e3713197f7dc665041d552ddf0cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#nullable enable

using System;
using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;

namespace Emby.Server.Implementations.Library
{
    /// <summary>
    /// Class providing extension methods for working with paths.
    /// </summary>
    public static class PathExtensions
    {
        /// <summary>
        /// Gets the attribute value.
        /// </summary>
        /// <param name="str">The STR.</param>
        /// <param name="attribute">The attrib.</param>
        /// <returns>System.String.</returns>
        /// <exception cref="ArgumentException"><paramref name="str" /> or <paramref name="attribute" /> is empty.</exception>
        public static string? GetAttributeValue(this string str, string attribute)
        {
            if (str.Length == 0)
            {
                throw new ArgumentException("String can't be empty.", nameof(str));
            }

            if (attribute.Length == 0)
            {
                throw new ArgumentException("String can't be empty.", nameof(attribute));
            }

            string srch = "[" + attribute + "=";
            int start = str.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
            if (start != -1)
            {
                start += srch.Length;
                int end = str.IndexOf(']', start);
                return str.Substring(start, end - start);
            }

            // for imdbid we also accept pattern matching
            if (string.Equals(attribute, "imdbid", StringComparison.OrdinalIgnoreCase))
            {
                var m = Regex.Match(str, "tt([0-9]{7,8})", RegexOptions.IgnoreCase);
                return m.Success ? m.Value : null;
            }

            return null;
        }

        /// <summary>
        /// Replaces a sub path with another sub path and normalizes the final path.
        /// </summary>
        /// <param name="path">The original path.</param>
        /// <param name="subPath">The original sub path.</param>
        /// <param name="newSubPath">The new sub path.</param>
        /// <param name="newPath">The result of the sub path replacement</param>
        /// <returns>The path after replacing the sub path.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="path" />, <paramref name="newSubPath" /> or <paramref name="newSubPath" /> is empty.</exception>
        public static bool TryReplaceSubPath(this string path, string subPath, string newSubPath, [NotNullWhen(true)] out string? newPath)
        {
            if (string.IsNullOrWhiteSpace(path))
            {
                throw new ArgumentNullException(nameof(path));
            }

            if (string.IsNullOrWhiteSpace(subPath))
            {
                throw new ArgumentNullException(nameof(subPath));
            }

            if (string.IsNullOrWhiteSpace(newSubPath))
            {
                throw new ArgumentNullException(nameof(newSubPath));
            }

            char oldDirectorySeparatorChar;
            char newDirectorySeparatorChar;
            // True normalization is still not possible https://github.com/dotnet/runtime/issues/2162
            // The reasoning behind this is that a forward slash likely means it's a Linux path and
            // so the whole path should be normalized to use / and vice versa for Windows (although Windows doesn't care much).
            if (newSubPath.Contains('/', StringComparison.Ordinal))
            {
                oldDirectorySeparatorChar = '\\';
                newDirectorySeparatorChar = '/';
            }
            else
            {
                oldDirectorySeparatorChar = '/';
                newDirectorySeparatorChar = '\\';
            }

            if (path.Contains(oldDirectorySeparatorChar, StringComparison.Ordinal))
            {
                path = path.Replace(oldDirectorySeparatorChar, newDirectorySeparatorChar);
            }

            if (subPath.Contains(oldDirectorySeparatorChar, StringComparison.Ordinal))
            {
                subPath = subPath.Replace(oldDirectorySeparatorChar, newDirectorySeparatorChar);
            }

            // We have to ensure that the sub path ends with a directory separator otherwise we'll get weird results
            // when the sub path matches a similar but in-complete subpath
            if (!subPath.EndsWith(newDirectorySeparatorChar))
            {
                subPath += newDirectorySeparatorChar;
            }

            if (newSubPath.Contains(oldDirectorySeparatorChar, StringComparison.Ordinal))
            {
                newSubPath = newSubPath.Replace(oldDirectorySeparatorChar, newDirectorySeparatorChar);
            }

            if (!newSubPath.EndsWith(newDirectorySeparatorChar))
            {
                newSubPath += newDirectorySeparatorChar;
            }

            if (!path.Contains(subPath, StringComparison.OrdinalIgnoreCase))
            {
                newPath = null;
                return false;
            }

            newPath = path.Replace(subPath, newSubPath, StringComparison.OrdinalIgnoreCase);
            return true;
        }
    }
}