Skip to content

Commit

Permalink
Merge branch 'MUMC_beta' into MUMC-Latest
Browse files Browse the repository at this point in the history
  • Loading branch information
terrelsa13 committed Jan 3, 2025
2 parents c4a46ca + 07d1385 commit c26e8dc
Show file tree
Hide file tree
Showing 40 changed files with 2,987 additions and 1,013 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ mumc_config_defaults.py
mumc_config.py*
mumc_config.yaml*
mumc_config.yml*
mumc_config*.yaml
mumc_config*.yml
launch.json
.orig

Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
# Multi-User Media Cleaner
Multi-User Media Cleaner aka MUMC (pronounced Mew-Mick) will query your Emby/Jellyfin server for the movies, tv episodes, audio tracks, and audiobooks in your libraries. Then delete media items you no longer want taking up disk space.

# Cool! How Do I Use This?
# Cool! How Do I Use MUMC?
Check out the **MUMC [WIKI](https://github.com/terrelsa13/MUMC/wiki)**.

# Install MUMC
* [Linux](https://github.com/terrelsa13/MUMC/wiki/Install#Linux)
* [Windows](https://github.com/terrelsa13/MUMC/wiki/Install#Windows)
* [Docker](https://github.com/terrelsa13/MUMC/wiki/Install#Docker)

# MUMC Branches
* [MUMC-Latest](https://github.com/terrelsa13/MUMC/tree/MUMC-Latest) - MUMC_v5 is the current and latest branch
* [MUMC_v4](https://github.com/terrelsa13/MUMC/tree/MUMC_v4) - legacy; no longer maintained
Expand Down
10 changes: 5 additions & 5 deletions mumc.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ def MUMC():
#inital dictionary setup
init_dict=initialize_mumc(get_current_directory(),Path(__file__).parent)

#remove old DEBUG if it exists
delete_debug_log(init_dict)

#parse command line options
cmdopt_dict=parse_command_line_options(init_dict)

#import config file
cfg,init_dict=importConfig(init_dict,cmdopt_dict)

#Look for missing subfolder Ids and add them
#after importing the config; remove old DEBUG if it exists
delete_debug_log(init_dict)

#look for missing subfolder Ids and add them
cfg=populate_config_with_subfolder_ids(cfg,init_dict)

#remember original config for when user wants to update existing config file
Expand Down Expand Up @@ -124,7 +124,7 @@ def MUMC():
print_cache_stats(cfg)

if (cfg['DEBUG'] == 255):
#show cache data (only when DEBUG == 255
#show cache data (only when DEBUG == 255)
cache_data_to_debug(cfg)

#show footer info
Expand Down
2 changes: 1 addition & 1 deletion mumc_modules/mumc_blacklist_whitelist.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from mumc_modules.mumc_output import appendTo_DEBUG_log
from mumc_modules.mumc_compare_items import get_isItemMatching
from mumc_modules.mumc_server_type import isEmbyServer
#from mumc_modules.mumc_server_type import isEmbyServer


def get_opposing_listing_type(listing_type):
Expand Down
91 changes: 56 additions & 35 deletions mumc_modules/mumc_builder_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,42 +25,46 @@ def create_library_dicts(the_dict):

for pathinfo in lib['LibraryOptions']['PathInfos']:
pathpos=lib['LibraryOptions']['PathInfos'].index(pathinfo)
temp_lib_dict['lib_id']=the_dict['all_libraries'][libpos][libraryGuid]
if (('CollectionType' in lib) and
(not (lib['CollectionType'] == ''))):
temp_lib_dict['collection_type']=lib['CollectionType']
else:
temp_lib_dict['collection_type']=None
if (('Path' in lib['LibraryOptions']['PathInfos'][pathpos]) and
(not (lib['LibraryOptions']['PathInfos'][pathpos]['Path'] == ''))):
temp_lib_dict['path']=lib['LibraryOptions']['PathInfos'][pathpos]['Path']
else:
temp_lib_dict['path']=None
if (('NetworkPath' in lib['LibraryOptions']['PathInfos'][pathpos]) and
(not (lib['LibraryOptions']['PathInfos'][pathpos]['NetworkPath'] == ''))):
temp_lib_dict['network_path']=lib['LibraryOptions']['PathInfos'][pathpos]['NetworkPath']
else:
temp_lib_dict['network_path']=None


if (not (isJellyfinServer(the_dict['admin_settings']['server']['brand']))):
for lib_info in the_dict['all_library_subfolders']:
for subfolder_info in lib_info['SubFolders']:
if (subfolder_info['Path'] == temp_lib_dict['path']):
temp_lib_dict['subfolder_id']=subfolder_info['Id']
break
else:
temp_lib_dict['subfolder_id']=None


temp_lib_dict['lib_enabled']=True
if (not (((lib['Name'] == 'Recordings') and (lib['LibraryOptions']['PathInfos'][pathpos]['Path'].endswith('recordings'))) or
((lib['Name'] == 'Collections') and (lib['CollectionType'] == 'boxsets')))):

temp_lib_dict['lib_id']=the_dict['all_libraries'][libpos][libraryGuid]
if (('CollectionType' in lib) and
(not (lib['CollectionType'] == ''))):
temp_lib_dict['collection_type']=lib['CollectionType']
else:
temp_lib_dict['collection_type']=None
if (('Path' in lib['LibraryOptions']['PathInfos'][pathpos]) and
(not (lib['LibraryOptions']['PathInfos'][pathpos]['Path'] == ''))):
temp_lib_dict['path']=lib['LibraryOptions']['PathInfos'][pathpos]['Path']
else:
temp_lib_dict['path']=None
if (('NetworkPath' in lib['LibraryOptions']['PathInfos'][pathpos]) and
(not (lib['LibraryOptions']['PathInfos'][pathpos]['NetworkPath'] == ''))):
temp_lib_dict['network_path']=lib['LibraryOptions']['PathInfos'][pathpos]['NetworkPath']
else:
temp_lib_dict['network_path']=None


if (not (isJellyfinServer(the_dict['admin_settings']['server']['brand']))):
for lib_info in the_dict['all_library_subfolders']:
for subfolder_info in lib_info['SubFolders']:
if (subfolder_info['Path'] == temp_lib_dict['path']):
temp_lib_dict['subfolder_id']=subfolder_info['Id']
break
else:
temp_lib_dict['subfolder_id']=None


temp_lib_dict['lib_enabled']=True

the_dict['all_libraries_list'].append(copy.deepcopy(temp_lib_dict))
the_dict['all_library_ids_list'].append(temp_lib_dict['lib_id'])
the_dict['all_library_paths_list'].append(temp_lib_dict['path'])
the_dict['all_library_network_paths_list'].append(temp_lib_dict['network_path'])
the_dict['all_library_collection_types_list'].append(temp_lib_dict['collection_type'])
the_dict['all_library_path_ids_list'].append(temp_lib_dict['subfolder_id'])
the_dict['all_libraries_list'].append(copy.deepcopy(temp_lib_dict))
the_dict['all_library_ids_list'].append(temp_lib_dict['lib_id'])
the_dict['all_library_paths_list'].append(temp_lib_dict['path'])
the_dict['all_library_network_paths_list'].append(temp_lib_dict['network_path'])
the_dict['all_library_collection_types_list'].append(temp_lib_dict['collection_type'])
the_dict['all_library_path_ids_list'].append(temp_lib_dict['subfolder_id'])

the_dict.pop('all_libraries')

Expand Down Expand Up @@ -314,9 +318,26 @@ def add_libraries_to_existing_users(the_dict):
the_dict['all_users_dict'][the_dict['prev_users_dict'].index(existing_user)][the_dict['opposing_listing_type']].append(the_dict['all_libraries_list'][all_lib_id_pos])
else:
for existing_lib_id in existing_lib_id_list:
exisiting_lib_id_index=existing_lib_id_list.index(existing_lib_id)
for all_lib_pos in range(len(the_dict['all_library_ids_list'])):
if (existing_lib_id == the_dict['all_library_ids_list'][all_lib_pos]):
if (not (the_dict['all_libraries_list'][all_lib_pos] in existing_lib_info_list)):
#remove "lib_enabled" before comparison
#"lib_enabled" is expected to be manually changed in the config and therefore should be ignored for the comparison
all_libraries_list_lib_enabled=the_dict['all_libraries_list'][all_lib_pos].pop('lib_enabled',None)
existing_lib_info_list_lib_enabled=existing_lib_info_list[exisiting_lib_id_index].pop('lib_enabled',None)

libraries_match=True
if (not (the_dict['all_libraries_list'][all_lib_pos] == existing_lib_info_list[exisiting_lib_id_index])):
libraries_match=False

#re-add "lib_enabled" after comparison
if (not (all_libraries_list_lib_enabled == None)):
the_dict['all_libraries_list'][all_lib_pos]['lib_enabled']=all_libraries_list_lib_enabled
if (not (existing_lib_info_list_lib_enabled == None)):
existing_lib_info_list[exisiting_lib_id_index]['lib_enabled']=existing_lib_info_list_lib_enabled

#if libraries do not match; add the "new" library for this user
if (not (libraries_match)):
the_dict['all_users_dict'][the_dict['prev_users_dict'].index(existing_user)][the_dict['opposing_listing_type']].append(the_dict['all_libraries_list'][all_lib_pos])
existing_lib_info_list.append(the_dict['all_libraries_list'][all_lib_pos])
existing_lib_id_list.append(the_dict['all_library_ids_list'][all_lib_pos])
Expand Down
4 changes: 2 additions & 2 deletions mumc_modules/mumc_compare_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@


def is_instance(the_element):
if not isinstance(the_element, Mapping):
if (not isinstance(the_element, Mapping)):
raise TypeError('keys_exist() expects a dict-like object as the first argument.')


def keys_exist(the_element, *keys_indexes):
if isinstance(the_element, Mapping):
if not keys_indexes:
if (not keys_indexes):
raise ValueError('keys_exist() expects at least one key/index argument.')

temp_element = the_element
Expand Down
51 changes: 38 additions & 13 deletions mumc_modules/mumc_config_builder.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from mumc_modules.mumc_output import print_byType
from mumc_modules.mumc_setup_questions import get_brand,get_url,get_port,get_base,get_admin_username,get_admin_password,get_library_setup_behavior,get_library_matching_behavior,get_tag_name,get_show_disabled_users,get_user_and_library_selection_type
from mumc_modules.mumc_setup_questions import get_brand,get_url,get_port,get_base,get_admin_username,get_admin_password,get_library_setup_behavior,get_library_matching_behavior,get_tag_name,get_show_disabled_users,get_user_and_library_selection_type,proceed_arr_setup,get_arr_url,get_arr_port,get_arr_api
from mumc_modules.mumc_key_authentication import authenticate_user_by_name
from mumc_modules.mumc_versions import get_script_version
from mumc_modules.mumc_console_info import print_all_media_disabled,build_new_config_setup_to_delete_media
from mumc_modules.mumc_console_info import print_all_media_disabled,built_new_config_not_setup_to_delete_media
from mumc_modules.mumc_configuration_yaml import yaml_configurationBuilder
from mumc_modules.mumc_config_updater import yaml_configurationUpdater
from mumc_modules.mumc_config_skeleton import setYAMLConfigSkeleton
Expand Down Expand Up @@ -99,15 +99,15 @@ def build_configuration_file(the_dict,orig_dict={}):
print('----------------------------------------------------------------------------------------')

#Initialize for compare with other tag to prevent using the same tag in both blacktag and whitetag
the_dict['advanced_settings']['blacktags']=[]
the_dict['advanced_settings']['whitetags']=[]
the_dict['advanced_settings']['blacktags']['global']=[]
the_dict['advanced_settings']['whitetags']['global']=[]

#ask user for global blacktag(s)
the_dict['advanced_settings']['blacktags']=get_tag_name('blacktag',the_dict['advanced_settings']['whitetags'])
the_dict['advanced_settings']['blacktags']['global']=get_tag_name('blacktag',the_dict['advanced_settings']['whitetags']['global'])
print('----------------------------------------------------------------------------------------')

#ask user for global whitetag(s)
the_dict['advanced_settings']['whitetags']=get_tag_name('whitetag',the_dict['advanced_settings']['blacktags'])
the_dict['advanced_settings']['whitetags']['global']=get_tag_name('whitetag',the_dict['advanced_settings']['blacktags']['global'])
print('----------------------------------------------------------------------------------------')

#Updating the config; Prepare to run the config editor
Expand Down Expand Up @@ -142,26 +142,51 @@ def build_configuration_file(the_dict,orig_dict={}):

print('----------------------------------------------------------------------------------------')

#Add Sonarr and Radarr API settings to MUMC
#arrDict={'Radarr':'7878','Sonarr':'8989','Lidarr':'8686','Readarr':'8787'}
arrDict={'Radarr':'7878','Sonarr':'8989'}

for arr in arrDict:
if (proceed_arr_setup(arr)):
#define *arr dict
the_dict['admin_settings']['media_managers'][arr.casefold()]={}
#enable *arr
the_dict['admin_settings']['media_managers'][arr.casefold()]['enabled']=True
#get *arr url
arr_url=get_arr_url(arr)
#get *arr port
arr_port=get_arr_port(arr,arrDict[arr])
#build *arr url
if (len(arr_port)):
#*arr url with port
the_dict['admin_settings']['media_managers'][arr.casefold()]['url']=arr_url + ':' + arr_port
else:
#*arr url without port
the_dict['admin_settings']['media_managers'][arr.casefold()]['url']=arr_url
#get *arr api
the_dict['admin_settings']['media_managers'][arr.casefold()]['api_key']=get_arr_api(arr)

print('----------------------------------------------------------------------------------------')

print('----------------------------------------------------------------------------------------')

#set REMOVE_FILES
the_dict['advanced_settings']['REMOVE_FILES']=False

print('----------------------------------------------------------------------------------------')

#Build and save yaml config file
#Build and save new yaml config file
if (not the_dict['advanced_settings']['UPDATE_CONFIG']):
yaml_configurationBuilder(the_dict)

try:
the_dict=getIsAnyMediaEnabled(the_dict)

if (the_dict['all_media_disabled']):
print_all_media_disabled(the_dict)

strings_list_to_print=''
strings_list_to_print=build_new_config_setup_to_delete_media(strings_list_to_print,the_dict)
try:
print_byType(strings_list_to_print,the_dict['advanced_settings']['console_controls']['warnings']['script']['show'],the_dict,the_dict['advanced_settings']['console_controls']['warnings']['script']['formatting'])
except:
print_byType(strings_list_to_print[0],True,the_dict,{'font':{'color':'','style':''},'background':{'color':''}})
strings_list_to_print=built_new_config_not_setup_to_delete_media('',the_dict)
print_byType(strings_list_to_print,the_dict['advanced_settings']['console_controls']['warnings']['script']['show'],the_dict,the_dict['formatting'])

#the exception
except (AttributeError, ModuleNotFoundError):
Expand Down
Loading

0 comments on commit c26e8dc

Please sign in to comment.