├── .editorconfig ├── .github └── workflows │ └── dotnet.yml ├── .gitignore ├── JellyTweaks.sln ├── Jellyfin.Plugin.JellyTweaks ├── Configuration │ ├── PluginConfiguration.cs │ └── configPage.html ├── Data │ ├── Tweak.cs │ ├── TweakFile.cs │ └── TweakSearching.cs ├── JellyTweaks.cs ├── Jellyfin.Plugin.JellyTweaks.csproj ├── Paths.cs ├── ScheduledTasks │ └── ApplyTweaks.cs ├── Tweaks │ ├── DefaultMaxPage.cs │ ├── DefaultTitle.cs │ └── EnableBackdropsByDefault.cs └── Utils │ └── TweakUtils.cs ├── LICENSE ├── README.md └── manifest.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # With more recent updates Visual Studio 2017 supports EditorConfig files out of the box 2 | # Visual Studio Code needs an extension: https://github.com/editorconfig/editorconfig-vscode 3 | # For emacs, vim, np++ and other editors, see here: https://github.com/editorconfig 4 | ############################### 5 | # Core EditorConfig Options # 6 | ############################### 7 | root = true 8 | # All files 9 | [*] 10 | indent_style = space 11 | indent_size = 4 12 | charset = utf-8 13 | trim_trailing_whitespace = true 14 | insert_final_newline = true 15 | end_of_line = lf 16 | max_line_length = off 17 | 18 | # YAML indentation 19 | [*.{yml,yaml}] 20 | indent_size = 2 21 | 22 | # XML indentation 23 | [*.{csproj,xml}] 24 | indent_size = 2 25 | 26 | ############################### 27 | # .NET Coding Conventions # 28 | ############################### 29 | [*.{cs,vb}] 30 | # Organize usings 31 | dotnet_sort_system_directives_first = true 32 | # this. preferences 33 | dotnet_style_qualification_for_field = false:silent 34 | dotnet_style_qualification_for_property = false:silent 35 | dotnet_style_qualification_for_method = false:silent 36 | dotnet_style_qualification_for_event = false:silent 37 | # Language keywords vs BCL types preferences 38 | dotnet_style_predefined_type_for_locals_parameters_members = true:silent 39 | dotnet_style_predefined_type_for_member_access = true:silent 40 | # Parentheses preferences 41 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent 42 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent 43 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent 44 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent 45 | # Modifier preferences 46 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent 47 | dotnet_style_readonly_field = true:suggestion 48 | # Expression-level preferences 49 | dotnet_style_object_initializer = true:suggestion 50 | dotnet_style_collection_initializer = true:suggestion 51 | dotnet_style_explicit_tuple_names = true:suggestion 52 | dotnet_style_null_propagation = true:suggestion 53 | dotnet_style_coalesce_expression = true:suggestion 54 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent 55 | dotnet_style_prefer_inferred_tuple_names = true:suggestion 56 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion 57 | dotnet_style_prefer_auto_properties = true:silent 58 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent 59 | dotnet_style_prefer_conditional_expression_over_return = true:silent 60 | 61 | ############################### 62 | # Naming Conventions # 63 | ############################### 64 | # Style Definitions (From Roslyn) 65 | 66 | # Non-private static fields are PascalCase 67 | dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion 68 | dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields 69 | dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style 70 | 71 | dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field 72 | dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected 73 | dotnet_naming_symbols.non_private_static_fields.required_modifiers = static 74 | 75 | dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case 76 | 77 | # Constants are PascalCase 78 | dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion 79 | dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants 80 | dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style 81 | 82 | dotnet_naming_symbols.constants.applicable_kinds = field, local 83 | dotnet_naming_symbols.constants.required_modifiers = const 84 | 85 | dotnet_naming_style.constant_style.capitalization = pascal_case 86 | 87 | # Static fields are camelCase and start with s_ 88 | dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion 89 | dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields 90 | dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style 91 | 92 | dotnet_naming_symbols.static_fields.applicable_kinds = field 93 | dotnet_naming_symbols.static_fields.required_modifiers = static 94 | 95 | dotnet_naming_style.static_field_style.capitalization = camel_case 96 | dotnet_naming_style.static_field_style.required_prefix = _ 97 | 98 | # Instance fields are camelCase and start with _ 99 | dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion 100 | dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields 101 | dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style 102 | 103 | dotnet_naming_symbols.instance_fields.applicable_kinds = field 104 | 105 | dotnet_naming_style.instance_field_style.capitalization = camel_case 106 | dotnet_naming_style.instance_field_style.required_prefix = _ 107 | 108 | # Locals and parameters are camelCase 109 | dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion 110 | dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters 111 | dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style 112 | 113 | dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local 114 | 115 | dotnet_naming_style.camel_case_style.capitalization = camel_case 116 | 117 | # Local functions are PascalCase 118 | dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion 119 | dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions 120 | dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style 121 | 122 | dotnet_naming_symbols.local_functions.applicable_kinds = local_function 123 | 124 | dotnet_naming_style.local_function_style.capitalization = pascal_case 125 | 126 | # By default, name items with PascalCase 127 | dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion 128 | dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members 129 | dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style 130 | 131 | dotnet_naming_symbols.all_members.applicable_kinds = * 132 | 133 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case 134 | 135 | ############################### 136 | # C# Coding Conventions # 137 | ############################### 138 | [*.cs] 139 | # var preferences 140 | csharp_style_var_for_built_in_types = true:silent 141 | csharp_style_var_when_type_is_apparent = true:silent 142 | csharp_style_var_elsewhere = true:silent 143 | # Expression-bodied members 144 | csharp_style_expression_bodied_methods = false:silent 145 | csharp_style_expression_bodied_constructors = false:silent 146 | csharp_style_expression_bodied_operators = false:silent 147 | csharp_style_expression_bodied_properties = true:silent 148 | csharp_style_expression_bodied_indexers = true:silent 149 | csharp_style_expression_bodied_accessors = true:silent 150 | # Pattern matching preferences 151 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 152 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 153 | # Null-checking preferences 154 | csharp_style_throw_expression = true:suggestion 155 | csharp_style_conditional_delegate_call = true:suggestion 156 | # Modifier preferences 157 | csharp_preferred_modifier_order = public, private, protected, internal, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, volatile, async:suggestion 158 | # Expression-level preferences 159 | csharp_prefer_braces = true:silent 160 | csharp_style_deconstructed_variable_declaration = true:suggestion 161 | csharp_prefer_simple_default_expression = true:suggestion 162 | csharp_style_pattern_local_over_anonymous_function = true:suggestion 163 | csharp_style_inlined_variable_declaration = true:suggestion 164 | 165 | ############################### 166 | # C# Formatting Rules # 167 | ############################### 168 | # New line preferences 169 | csharp_new_line_before_open_brace = all 170 | csharp_new_line_before_else = true 171 | csharp_new_line_before_catch = true 172 | csharp_new_line_before_finally = true 173 | csharp_new_line_before_members_in_object_initializers = true 174 | csharp_new_line_before_members_in_anonymous_types = true 175 | csharp_new_line_between_query_expression_clauses = true 176 | # Indentation preferences 177 | csharp_indent_case_contents = true 178 | csharp_indent_switch_labels = true 179 | csharp_indent_labels = flush_left 180 | # Space preferences 181 | csharp_space_after_cast = false 182 | csharp_space_after_keywords_in_control_flow_statements = true 183 | csharp_space_between_method_call_parameter_list_parentheses = false 184 | csharp_space_between_method_declaration_parameter_list_parentheses = false 185 | csharp_space_between_parentheses = false 186 | csharp_space_before_colon_in_inheritance_clause = true 187 | csharp_space_after_colon_in_inheritance_clause = true 188 | csharp_space_around_binary_operators = before_and_after 189 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 190 | csharp_space_between_method_call_name_and_opening_parenthesis = false 191 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 192 | # Wrapping preferences 193 | csharp_preserve_single_line_statements = true 194 | csharp_preserve_single_line_blocks = true 195 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | main: 11 | runs-on: ubuntu-latest 12 | steps: 13 | 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | with: 17 | fetch-depth: 0 18 | 19 | - name: Install .NET Core 20 | uses: actions/setup-dotnet@v1 21 | with: 22 | dotnet-version: 8.0.x 23 | 24 | - name: Dependencies 25 | run: dotnet restore 26 | 27 | - name: Build 28 | run: dotnet build --configuration release 29 | 30 | - name: Upload 31 | uses: actions/upload-artifact@v3.1.2 32 | with: 33 | artifacts: Jellyfin.Plugin.JellyTweaks 34 | path: Jellyfin.Plugin.JellyTweaks/bin/*/*/Jellyfin.Plugin.JellyTweaks.dll 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cr/ 2 | .directory 3 | 4 | ################# 5 | ## Eclipse 6 | ################# 7 | 8 | *.pydevproject 9 | .project 10 | .metadata 11 | bin/ 12 | tmp/ 13 | *.tmp 14 | *.bak 15 | *.swp 16 | *~.nib 17 | project.fragment.lock.json 18 | project.lock.json 19 | local.properties 20 | .classpath 21 | .settings/ 22 | .loadpath 23 | 24 | # External tool builders 25 | .externalToolBuilders/ 26 | 27 | # Locally stored "Eclipse launch configurations" 28 | *.launch 29 | 30 | # CDT-specific 31 | .cproject 32 | 33 | # PDT-specific 34 | .buildpath 35 | 36 | ################# 37 | ## Media Browser 38 | ################# 39 | ProgramData*/ 40 | CorePlugins*/ 41 | ProgramData-Server*/ 42 | ProgramData-UI*/ 43 | 44 | ################# 45 | ## Visual Studio 46 | ################# 47 | 48 | ## Ignore Visual Studio temporary files, build results, and 49 | ## files generated by popular Visual Studio add-ons. 50 | 51 | .vs/ 52 | 53 | # User-specific files 54 | *.suo 55 | *.user 56 | *.sln.docstates 57 | 58 | # Build results 59 | 60 | [Dd]ebug/ 61 | [Rr]elease/ 62 | build/ 63 | [Bb]in/ 64 | [Oo]bj/ 65 | 66 | # MSTest test Results 67 | [Tt]est[Rr]esult*/ 68 | [Bb]uild[Ll]og.* 69 | 70 | *_i.c 71 | *_p.c 72 | *.ilk 73 | *.meta 74 | *.obj 75 | *.pch 76 | *.pdb 77 | *.pgc 78 | *.pgd 79 | *.rsp 80 | *.sbr 81 | *.tlb 82 | *.tli 83 | *.tlh 84 | *.tmp 85 | *.tmp_proj 86 | *.log 87 | *.vspscc 88 | *.vssscc 89 | .builds 90 | *.pidb 91 | *.log 92 | *.scc 93 | *.scc 94 | *.psess 95 | *.vsp 96 | *.vspx 97 | *.orig 98 | *.rej 99 | *.sdf 100 | *.opensdf 101 | *.ipch 102 | 103 | # Visual C++ cache files 104 | ipch/ 105 | *.aps 106 | *.ncb 107 | *.opensdf 108 | *.sdf 109 | *.cachefile 110 | 111 | # Visual Studio profiler 112 | *.psess 113 | *.vsp 114 | *.vspx 115 | 116 | # Guidance Automation Toolkit 117 | *.gpState 118 | 119 | # ReSharper is a .NET coding add-in 120 | _ReSharper*/ 121 | *.[Rr]e[Ss]harper 122 | 123 | # TeamCity is a build add-in 124 | _TeamCity* 125 | 126 | # DotCover is a Code Coverage Tool 127 | *.dotCover 128 | 129 | # NCrunch 130 | *.ncrunch* 131 | .*crunch*.local.xml 132 | 133 | # Installshield output folder 134 | [Ee]xpress/ 135 | 136 | # DocProject is a documentation generator add-in 137 | DocProject/buildhelp/ 138 | DocProject/Help/*.HxT 139 | DocProject/Help/*.HxC 140 | DocProject/Help/*.hhc 141 | DocProject/Help/*.hhk 142 | DocProject/Help/*.hhp 143 | DocProject/Help/Html2 144 | DocProject/Help/html 145 | 146 | # Click-Once directory 147 | publish/ 148 | 149 | # Publish Web Output 150 | *.Publish.xml 151 | *.pubxml 152 | 153 | # NuGet Packages Directory 154 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 155 | # packages/ 156 | dlls/ 157 | dllssigned/ 158 | 159 | # Windows Azure Build Output 160 | csx 161 | *.build.csdef 162 | 163 | # Windows Store app package directory 164 | AppPackages/ 165 | 166 | # Others 167 | sql/ 168 | *.Cache 169 | ClientBin/ 170 | [Ss]tyle[Cc]op.* 171 | ~$* 172 | *~ 173 | *.dbmdl 174 | *.[Pp]ublish.xml 175 | *.publishsettings 176 | 177 | # RIA/Silverlight projects 178 | Generated_Code/ 179 | 180 | # Backup & report files from converting an old project file to a newer 181 | # Visual Studio version. Backup files are not needed, because we have git ;-) 182 | _UpgradeReport_Files/ 183 | Backup*/ 184 | UpgradeLog*.XML 185 | UpgradeLog*.htm 186 | 187 | # SQL Server files 188 | App_Data/*.mdf 189 | App_Data/*.ldf 190 | 191 | ############# 192 | ## Windows detritus 193 | ############# 194 | 195 | # Windows image file caches 196 | Thumbs.db 197 | ehthumbs.db 198 | 199 | # Folder config file 200 | Desktop.ini 201 | 202 | # Recycle Bin used on file shares 203 | $RECYCLE.BIN/ 204 | 205 | # Mac crap 206 | .DS_Store 207 | 208 | ############# 209 | ## Python 210 | ############# 211 | 212 | *.py[co] 213 | 214 | # Packages 215 | *.egg 216 | *.egg-info 217 | build/ 218 | eggs/ 219 | parts/ 220 | var/ 221 | sdist/ 222 | develop-eggs/ 223 | .installed.cfg 224 | 225 | # Installer logs 226 | pip-log.txt 227 | 228 | # Unit test / coverage reports 229 | .coverage 230 | .tox 231 | 232 | #Translations 233 | *.mo 234 | 235 | #Mr Developer 236 | .mr.developer.cfg 237 | 238 | ########## 239 | # Rider 240 | ########## 241 | .idea/ 242 | 243 | ######################### 244 | # Build artifacts 245 | ######################### 246 | 247 | # Artifacts for debian-x64 248 | debian/.debhelper/ 249 | debian/*.debhelper 250 | debian/debhelper-build-stamp 251 | debian/files 252 | debian/jellyfin.substvars 253 | debian/jellyfin/ 254 | # Don't ignore the debian/bin folder 255 | !debian/bin/ 256 | 257 | deployment/**/dist/ 258 | deployment/**/pkg-dist/ 259 | deployment/**/pkg-dist-tmp/ 260 | deployment/collect-dist/ 261 | 262 | jellyfin_version.ini 263 | 264 | ci/ 265 | 266 | # Doxygen 267 | doc/ 268 | 269 | # Deployment artifacts 270 | dist 271 | *.exe 272 | *.dll 273 | 274 | # BenchmarkDotNet artifacts 275 | BenchmarkDotNet.Artifacts 276 | 277 | # Ignore web artifacts from native builds 278 | web/ 279 | web-src.* 280 | MediaBrowser.WebDashboard/jellyfin-web 281 | apiclient/generated 282 | 283 | # Omnisharp crash logs 284 | mono_crash.*.json 285 | -------------------------------------------------------------------------------- /JellyTweaks.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.5.33414.496 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Plugin.JellyTweaks", "Jellyfin.Plugin.JellyTweaks\Jellyfin.Plugin.JellyTweaks.csproj", "{D921B930-CF91-406F-ACBC-08914DCD0D34}" 6 | EndProject 7 | Global 8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 9 | Debug|Any CPU = Debug|Any CPU 10 | Debug|x64 = Debug|x64 11 | Release|Any CPU = Release|Any CPU 12 | Release|x64 = Release|x64 13 | EndGlobalSection 14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 15 | {D921B930-CF91-406F-ACBC-08914DCD0D34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 16 | {D921B930-CF91-406F-ACBC-08914DCD0D34}.Debug|Any CPU.Build.0 = Debug|Any CPU 17 | {D921B930-CF91-406F-ACBC-08914DCD0D34}.Debug|x64.ActiveCfg = Debug|Any CPU 18 | {D921B930-CF91-406F-ACBC-08914DCD0D34}.Debug|x64.Build.0 = Debug|Any CPU 19 | {D921B930-CF91-406F-ACBC-08914DCD0D34}.Release|Any CPU.ActiveCfg = Release|Any CPU 20 | {D921B930-CF91-406F-ACBC-08914DCD0D34}.Release|Any CPU.Build.0 = Release|Any CPU 21 | {D921B930-CF91-406F-ACBC-08914DCD0D34}.Release|x64.ActiveCfg = Release|Any CPU 22 | {D921B930-CF91-406F-ACBC-08914DCD0D34}.Release|x64.Build.0 = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | GlobalSection(ExtensibilityGlobals) = postSolution 28 | SolutionGuid = {1731C3EE-64BA-45C0-8295-63CEEB49AF1B} 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /Jellyfin.Plugin.JellyTweaks/Configuration/PluginConfiguration.cs: -------------------------------------------------------------------------------- 1 | using MediaBrowser.Model.Plugins; 2 | 3 | namespace Jellyfin.Plugin.JellyTweaks.Configuration; 4 | 5 | /// 6 | /// Plugin configuration. 7 | /// 8 | public class PluginConfiguration : BasePluginConfiguration 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | public PluginConfiguration() 14 | { 15 | EnableBackdropsByDefault = false; 16 | DefaultLibraryPageSize = 100; 17 | DefaultTitle = "Jellyfin"; 18 | } 19 | 20 | /// 21 | /// Gets or sets a value of default title. 22 | /// 23 | public string DefaultTitle { get; set; } 24 | 25 | /// 26 | /// Gets or sets a value indicating whether backdrops is enabled by default. 27 | /// 28 | public bool EnableBackdropsByDefault { get; set; } 29 | 30 | /// 31 | /// Gets or sets a value of default page size. 32 | /// 33 | public int DefaultLibraryPageSize { get; set; } 34 | } 35 | -------------------------------------------------------------------------------- /Jellyfin.Plugin.JellyTweaks/Configuration/configPage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JellyTweaks 6 | 7 | 8 |
9 |
10 |
11 |
12 | 13 |
14 | 15 | 16 |
Default value 'Jellyfin'
17 |
18 | 19 |
20 | 24 |
25 | 26 |
27 | 28 | 29 |
Default value '100'
30 |
31 | 32 |
33 | 36 |
37 | 38 |
39 |
40 |
41 | 72 |
73 | 74 | 75 | -------------------------------------------------------------------------------- /Jellyfin.Plugin.JellyTweaks/Data/Tweak.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using Jellyfin.Plugin.JellyTweaks.Configuration; 3 | 4 | namespace Jellyfin.Plugin.JellyTweaks.Data; 5 | 6 | public class Tweak(string name, Collection files) 7 | { 8 | public string Name { get; } = name; 9 | 10 | public Collection Files { get; } = files; 11 | 12 | public virtual async Task Execute(PluginConfiguration configuration) 13 | { 14 | throw new NotImplementedException(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Jellyfin.Plugin.JellyTweaks/Data/TweakFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | 3 | namespace Jellyfin.Plugin.JellyTweaks.Data; 4 | 5 | public class TweakFile(string path, Collection searchingValues) 6 | { 7 | public string Path { get; } = path; 8 | 9 | public Collection SearchingValues { get; } = searchingValues; 10 | } 11 | -------------------------------------------------------------------------------- /Jellyfin.Plugin.JellyTweaks/Data/TweakSearching.cs: -------------------------------------------------------------------------------- 1 | namespace Jellyfin.Plugin.JellyTweaks.Data; 2 | 3 | public class TweakSearching(string start, string end) 4 | { 5 | public string Start { get; } = start; 6 | 7 | public string End { get; } = end; 8 | } 9 | -------------------------------------------------------------------------------- /Jellyfin.Plugin.JellyTweaks/JellyTweaks.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using Jellyfin.Plugin.JellyTweaks.Configuration; 3 | using MediaBrowser.Common.Configuration; 4 | using MediaBrowser.Common.Plugins; 5 | using MediaBrowser.Model.Plugins; 6 | using MediaBrowser.Model.Serialization; 7 | 8 | namespace Jellyfin.Plugin.JellyTweaks; 9 | 10 | public class JellyTweaks : BasePlugin, IHasWebPages 11 | { 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | /// Instance of the interface. 16 | /// Instance of the interface. 17 | public JellyTweaks(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) : base(applicationPaths, xmlSerializer) 18 | { 19 | Instance = this; 20 | 21 | // Check if webPath is not null 22 | if (string.IsNullOrWhiteSpace(applicationPaths.WebPath)) 23 | { 24 | return; 25 | } 26 | 27 | Paths.IndexHtml = Path.Combine(applicationPaths.WebPath, "index.html"); 28 | Paths.MainJs = Path.Combine(applicationPaths.WebPath, "main.jellyfin.bundle.js"); 29 | } 30 | 31 | /// 32 | public override string Name => "JellyTweaks"; 33 | 34 | /// 35 | public override Guid Id => Guid.Parse("dfee3828-01df-49df-85b1-5c2b75e5ea1a"); 36 | 37 | /// 38 | /// Gets the current plugin instance. 39 | /// 40 | public static JellyTweaks? Instance { get; private set; } 41 | 42 | /// 43 | public IEnumerable GetPages() 44 | { 45 | return 46 | [ 47 | new PluginPageInfo { Name = Name, EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Configuration.configPage.html", GetType().Namespace) } 48 | ]; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Jellyfin.Plugin.JellyTweaks/Jellyfin.Plugin.JellyTweaks.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 1.0.0.4 8 | 1.0.0.4 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Jellyfin.Plugin.JellyTweaks/Paths.cs: -------------------------------------------------------------------------------- 1 | namespace Jellyfin.Plugin.JellyTweaks; 2 | 3 | public static class Paths 4 | { 5 | /// 6 | /// Gets or sets 'index.html' path. 7 | /// 8 | public static string? IndexHtml { get; set; } 9 | 10 | /// 11 | /// Gets or sets 'main.jellyfin.bundle.js' path. 12 | /// 13 | public static string? MainJs { get; set; } 14 | } 15 | -------------------------------------------------------------------------------- /Jellyfin.Plugin.JellyTweaks/ScheduledTasks/ApplyTweaks.cs: -------------------------------------------------------------------------------- 1 | using Jellyfin.Plugin.JellyTweaks.Data; 2 | using Jellyfin.Plugin.JellyTweaks.Tweaks; 3 | using MediaBrowser.Model.Globalization; 4 | using MediaBrowser.Model.Tasks; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace Jellyfin.Plugin.JellyTweaks.ScheduledTasks; 8 | 9 | public class ApplyTweaks(ILoggerFactory loggerFactory, ILocalizationManager localization) : IScheduledTask 10 | { 11 | /// 12 | public string Name => "Apply Tweaks"; 13 | 14 | /// 15 | public string Key => "ApplyTweaks"; 16 | 17 | /// 18 | public string Description => "Apply tweaks enabled in settings."; 19 | 20 | /// 21 | public string Category => localization.GetLocalizedString("TasksLibraryCategory"); 22 | 23 | public async Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken) 24 | { 25 | var logger = loggerFactory.CreateLogger(); 26 | var config = JellyTweaks.Instance!.Configuration; 27 | 28 | if (config == null) 29 | { 30 | throw new InvalidOperationException("Configuration cannot be null"); 31 | } 32 | 33 | await new EnableBackdropsByDefault(logger).Execute(config); 34 | await new DefaultMaxPage(logger).Execute(config); 35 | await new DefaultTitle(logger).Execute(config); 36 | 37 | cancellationToken.ThrowIfCancellationRequested(); 38 | } 39 | 40 | public IEnumerable GetDefaultTriggers() 41 | { 42 | return 43 | [ 44 | new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerStartup } 45 | ]; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Jellyfin.Plugin.JellyTweaks/Tweaks/DefaultMaxPage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using System.Globalization; 3 | using Jellyfin.Plugin.JellyTweaks.Configuration; 4 | using Jellyfin.Plugin.JellyTweaks.Data; 5 | using Jellyfin.Plugin.JellyTweaks.Utils; 6 | using Microsoft.Extensions.Logging; 7 | 8 | namespace Jellyfin.Plugin.JellyTweaks.Tweaks; 9 | 10 | public class DefaultMaxPage(ILogger logger) : Tweak(Name, _files) 11 | { 12 | private new const string Name = "DefaultLibraryPageSize"; 13 | 14 | private static readonly Collection _files = 15 | [ 16 | new TweakFile(Paths.MainJs!, 17 | [ 18 | new TweakSearching("this.get(\"libraryPageSize\",!1),10);return 0===t?0:t||", "}") 19 | ]) 20 | ]; 21 | 22 | public override async Task Execute(PluginConfiguration configuration) 23 | { 24 | var value = Math.Abs(configuration.DefaultLibraryPageSize).ToString(CultureInfo.InvariantCulture); 25 | await TweakUtils.ApplyTweakAsync(logger, this, value).ConfigureAwait(false); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Jellyfin.Plugin.JellyTweaks/Tweaks/DefaultTitle.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using Jellyfin.Plugin.JellyTweaks.Configuration; 3 | using Jellyfin.Plugin.JellyTweaks.Data; 4 | using Jellyfin.Plugin.JellyTweaks.Utils; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace Jellyfin.Plugin.JellyTweaks.Tweaks; 8 | 9 | // TODO: Fix for 10.9.x 10 | public class DefaultTitle(ILogger logger) : Tweak(Name, _files) 11 | { 12 | private new const string Name = "DefaultTitle"; 13 | 14 | private static readonly Collection _files = 15 | [ 16 | // new TweakFile(Paths.MainJs!, 17 | // [ 18 | // new TweakSearching("document.title=\"", "\"}"), 19 | // new TweakSearching("document.title=e||\"", "\"}") 20 | // ]), 21 | new TweakFile(Paths.IndexHtml!, 22 | [ 23 | new TweakSearching("", ""), 24 | //new TweakSearching(""), 25 | //new TweakSearching(""), 26 | new TweakSearching("") 27 | ]) 28 | ]; 29 | 30 | public override async Task Execute(PluginConfiguration configuration) 31 | { 32 | var value = configuration.DefaultTitle; 33 | await TweakUtils.ApplyTweakAsync(logger, this, value).ConfigureAwait(false); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Jellyfin.Plugin.JellyTweaks/Tweaks/EnableBackdropsByDefault.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using Jellyfin.Plugin.JellyTweaks.Configuration; 3 | using Jellyfin.Plugin.JellyTweaks.Data; 4 | using Jellyfin.Plugin.JellyTweaks.Utils; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace Jellyfin.Plugin.JellyTweaks.Tweaks; 8 | 9 | public class EnableBackdropsByDefault(ILogger logger) : Tweak(Name, _files) 10 | { 11 | private new const string Name = "EnableBackdropsByDefault"; 12 | 13 | private static readonly Collection _files = 14 | [ 15 | new TweakFile(Paths.MainJs!, [ 16 | new TweakSearching("enableBackdrops:function(){return ", "}") 17 | ]) 18 | ]; 19 | 20 | public override async Task Execute(PluginConfiguration configuration) 21 | { 22 | var value = configuration.EnableBackdropsByDefault ? "P" : "_"; 23 | await TweakUtils.ApplyTweakAsync(logger, this, value).ConfigureAwait(false); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Jellyfin.Plugin.JellyTweaks/Utils/TweakUtils.cs: -------------------------------------------------------------------------------- 1 | using Jellyfin.Plugin.JellyTweaks.Data; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace Jellyfin.Plugin.JellyTweaks.Utils; 5 | 6 | public static class TweakUtils 7 | { 8 | public static async Task ApplyTweakAsync(ILogger logger, Tweak tweak, string value) 9 | { 10 | var isChanged = false; 11 | 12 | foreach (var file in tweak.Files) 13 | { 14 | var path = file.Path; 15 | if (!File.Exists(path)) 16 | { 17 | logger.LogError($"Cannot find path {path} for apply {tweak.Name}!"); 18 | return; 19 | } 20 | 21 | var originalContent = await File.ReadAllTextAsync(path); 22 | var content = originalContent; 23 | 24 | // Find and change values 25 | foreach (var values in file.SearchingValues) 26 | { 27 | var start = values.Start; 28 | var end = values.End; 29 | 30 | try 31 | { 32 | var startIndex = content.IndexOf(start, StringComparison.Ordinal); 33 | var endIndex = content.IndexOf(end, startIndex, StringComparison.Ordinal); 34 | 35 | if (startIndex == -1 || endIndex == -1) 36 | { 37 | logger.LogError($"Cannot find values '{start}xxx{end}'!"); 38 | continue; 39 | } 40 | 41 | var original = content[startIndex..(endIndex + end.Length)]; 42 | var changed = $"{start}{value}{end}"; 43 | 44 | content = content.Replace(original, changed, StringComparison.Ordinal); 45 | } 46 | catch (ArgumentOutOfRangeException ex) 47 | { 48 | logger.LogError($"Cannot find values '{start}xxx{end}'! | Error: {ex.Message}"); 49 | } 50 | } 51 | 52 | // Continue if no modifications 53 | if (originalContent.Equals(content, StringComparison.Ordinal)) 54 | { 55 | continue; 56 | } 57 | 58 | // Save changes to file 59 | try 60 | { 61 | await File.WriteAllTextAsync(path, content).ConfigureAwait(false); 62 | isChanged = true; 63 | } 64 | catch (Exception ex) 65 | { 66 | logger.LogError($"Encountered exception while applying tweak {tweak.Name}: {ex}"); 67 | return; 68 | } 69 | } 70 | 71 | logger.LogInformation(isChanged ? $"Finished applying tweak: {tweak.Name}!" : $"Tweak {tweak.Name} no need to apply!"); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 gam24 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JellyTweaks 2 | ==================== 3 | 4 | ## About ## 5 | A simple program that adds useful features 6 | 7 | Abilities 8 | * Change default title 9 | * Change default library page size 10 | * Enabling backdrops by default 11 | 12 | Warning 13 | * Plugin edits "index.html" and "main.jellyfin.bundle.js" files, it is recommended to back up both files. 14 | * If user doesn't have the appropriate permissions, the plugin will not be able to change the settings. 15 | 16 | ## Installation ## 17 | 1. Add "https://raw.githubusercontent.com/gaam24/JellyTweaks/main/manifest.json" to Jellyfin plugin repository 18 | 2. Install JellyTweaks from the repository 19 | 3. Restart the Jellyfin server 20 | 4. If Jellyfin's web path is set this should all work fine 21 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "JellyTweaks", 4 | "guid": "dfee3828-01df-49df-85b1-5c2b75e5ea1a", 5 | "description": "Tweaks which may be useful", 6 | "overview": "Tweaks for Jellyfin", 7 | "owner": "gam24", 8 | "category": "General", 9 | "versions": [ 10 | { 11 | "checksum": "025fee84c36e0146ed8c8c59c93e933f", 12 | "changelog": "- Fixed bug with apply tweeks", 13 | "targetAbi": "10.9.4.0", 14 | "sourceUrl": "https://github.com/gaam24/JellyTweaks/releases/download/1.0.0.4/Jellyfin.Plugin.JellyTweaks.zip", 15 | "timestamp": "2024-06-07T00:00:00Z", 16 | "version": "1.0.0.4" 17 | }, 18 | { 19 | "checksum": "5fb9f1715d69a2061d84fde2e2a12c42", 20 | "changelog": " - Fixed enableBackdrops by default\n - Update to .NET 8.0\n - Code cleanup", 21 | "targetAbi": "10.9.4.0", 22 | "sourceUrl": "https://github.com/gaam24/JellyTweaks/releases/download/1.0.0.3/Jellyfin.Plugin.JellyTweaks.zip", 23 | "timestamp": "2024-06-03T00:00:00Z", 24 | "version": "1.0.0.3" 25 | }, 26 | { 27 | "checksum": "41d9555a72b9cf6fe9abdcefa4ba9b5a", 28 | "changelog": " - Fix CultureInfo", 29 | "targetAbi": "10.8.9.0", 30 | "sourceUrl": "https://github.com/gaam24/JellyTweaks/releases/download/1.0.0.2/Jellyfin.Plugin.JellyTweaks.zip", 31 | "timestamp": "2023-04-24T00:00:00Z", 32 | "version": "1.0.0.2" 33 | }, 34 | { 35 | "checksum": "ac7206b63ee2ca9361c765ce15226800", 36 | "changelog": " - Rebuilded tweak searching (now using all tweaks for one file saving the file only once)\n - Added descriptions in options\n - Small code cleanup", 37 | "targetAbi": "10.8.9.0", 38 | "sourceUrl": "https://github.com/gaam24/JellyTweaks/releases/download/1.0.0.1/Jellyfin.Plugin.JellyTweaks.zip", 39 | "timestamp": "2023-02-28T00:00:00Z", 40 | "version": "1.0.0.1" 41 | }, 42 | { 43 | "checksum": "a2ac74f5e84bfdb6a6cd7c8648e5e161", 44 | "changelog": "Initial release", 45 | "targetAbi": "10.8.9.0", 46 | "sourceUrl": "https://github.com/gaam24/JellyTweaks/releases/download/1.0.0.0/Jellyfin.Plugin.JellyTweaks.zip", 47 | "timestamp": "2023-02-27T00:00:00Z", 48 | "version": "1.0.0.0" 49 | } 50 | ] 51 | } 52 | ] 53 | --------------------------------------------------------------------------------