Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PICARD-2025: Add file path to metadata view #2552

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

zytact
Copy link
Contributor

@zytact zytact commented Nov 24, 2024

Summary

  • This is a…
    • Bug fix
    • Feature addition
    • Refactoring
    • Minor / simple change (like a typo)
    • Other

Problem

Adding File Path to metadata view

Solution

Added a Path key to the metadata dictionary which gets the file name from ~new_path and ~old_path key .

Copy link
Contributor

@Sophist-UK Sophist-UK left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aside from the pylint issue, the code looks fine to me.

(With the benefit of hindsight it might have been helpful to split the changes into two commits (one for the non-functional formatting changes, and one for the actual changes for this PR, but it was still possible to review this without these being separated.)

Before this gets merged and sets the metadata names in stone to preserve backwards compatibility, I do wonder whether the variables should be called ["path_old", "~path_saved"] rather than ["~old_path", "~new_path"].

Finally there is nothing wrong with this requirement, but these are hidden variables.

  1. I am not sure that this PR should have "metadata view" in the name if the variables are not viewable in the UI.
  2. A separate requirement (and so not for this PR) is IMO to have both the current file location / filename displayed in the old metadata and where it will be saved if you do a save (i.e. taking into account the Move and Rename options) in the new metadata (with the standard value-changed colouring and album icons applied).

picard/file.py Outdated
log.debug("Loading %r failed, retrying as %r", self, alternative_file)
if type(alternative_file) != type(
self
): # pylint: disable=unidiomatic-typecheck # noqa: E721
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are going to split this code into separate lines, then you need to move the # pylint to the correct line.

Copy link
Collaborator

@zas zas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are too many changes that are totally unrelated to the feature you want to implement.
Please do not change string quotes without a good reason to do it, read https://github.com/metabrainz/picard/blob/master/CONTRIBUTING.md#strings-quoting-single-or-double-quotes

Also, re-indentation, line wrapping, etc, should be done in a separate PR (if really needed).

@Sophist-UK
Copy link
Contributor

@zas I guessed that you would probably but understandably say this. In @zytact's defense, it was probably his code editor settings doing it automatically. But of course, had he put these changes in a separate commit, rebasing to remove the commit would have been easy-peasy.

@zytact
Copy link
Contributor Author

zytact commented Nov 24, 2024

Yes, that was a mistake on my part. I mistakenly turned on format on save and didn't notice. I apologize. I am new to this and seem to have somehow closed the pull request, should I properly do a new one?

@zas
Copy link
Collaborator

zas commented Nov 24, 2024

somehow closed the pull request, should I properly do a new one?

You can just push your commits on your path-metadata branch and we can re-open this PR.

@Sophist-UK
Copy link
Contributor

The force push you just made removed every single change. Is that what you intended?

@zytact
Copy link
Contributor Author

zytact commented Nov 24, 2024

Yes that was intended. I wanted to clear the git history. I just pushed the new changes.

@zytact zytact reopened this Nov 24, 2024
@@ -367,6 +367,7 @@ def _preserve_times(self, filename, func):

def _save_and_rename(self, old_filename, metadata):
"""Save the metadata."""
metadata["path_old"] = old_filename
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"~" missing.

if not (files or tracks):
return None
for file in files:
old_path = file.metadata.get('path_old', file.filename)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing "~".

files = self.files
tracks = self.tracks
self.selection_mutex.unlock()
try:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have you introduced a try block here? What exceptions are you expecting?

There is no except statement - so what happens if there is an exception in the try section?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for pointing that out, I forgot to add the except block but I simply couldn't get it to work on my machine without using a try block. I'll look into it properly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presumably you were getting an exception without the try block and this is now hidden. This is really not a good idea.

However, please see later comments as you may want to hold off from spending any more time on this code until there is consensus on what is actually needed.

Comment on lines +577 to +578
if new_selection or drop_album_caches:
self._single_file_album = len({file.metadata['album'] for file in files}) == 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to have been included in the try block in error since it still exists a few lines further down.

Comment on lines +572 to +575
file.orig_metadata['Path'] = old_path

new_path = file.metadata.get('~path_saved', file.filename)
file.metadata['Path'] = new_path
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have introduced a new metadata tag here and there are various issues with this:

  1. I think you should split this into path and filename so that we can compare the two separately - and so that they relate directly to the Move and Rename options.
  2. This is NOT a tag that should be saved to the file, so it should be ~path and ~filename. Path may be saved as a miscellaneous text tag in some formats that support this (e.g. ID3).
  3. You will need to make sure that these metadata variables are recalculated if the user changes Move or Rename options.
  4. You will need to change the metadata table generation to add rows for ~path and ~filename and ensure that they are used when comparing old and new metadata and when summarising metadata over several albums or tracks.
  5. You will need to add definitions for Path and Filename metadata elsewhere so that the names get translated.
  6. You may need to write additional tests.

None of the above are major pieces of work, but they do need to be done.

@rdswift
Copy link
Collaborator

rdswift commented Nov 24, 2024

Assuming this is eventually going to be accepted and merged, please add a ticket to update the Picard documentation to include the new tags/variables. When adding the new ticket, please reference the ticket that you're addressing in this PR so that they are linked. Thanks.

Comment on lines +416 to +419
if error is None and result is not None:
new_filename = result
# Add new path info after successful rename
self.metadata["~path_saved"] = new_filename
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I must be missing something because it appears that the "~path_saved" variable is being set after the file has been saved. Can you provide an example of how this would be used?

Copy link
Contributor

@Sophist-UK Sophist-UK Nov 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that Bob has prompted me to think further, it seems to me that we need to know:

  1. The current path i.e. where the file actually is - displayed in Old metadata.
  2. The future path if we were to save the file now - displayed in New metadata.
  3. The previous path if we have saved the file since it has been loaded.

To make life easiest for scripters, once a file is loaded and before it is saved we need to decide whether the previous path is set to the current path or left empty / non-existent.

And we should decide whether to save the entire path (directory path and filename as a string) or the directory path and the filename separately or possibly both. (Scripters may want directory and filename separate or they may want it together. UI needs them separate.)

Motivation: We should try to get this exactly right from the start before we create a backwards compatibility problem for scripters.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3. The previous path if we have saved the file since it has been loaded.

Do we need this? When the file is saved, I think the file variables are automatically updated to the new path and filename, so I'm not sure what the use case would be for the original path and filename.

And we should decide whether to save the entire path (directory path and filename as a string) or the directory path and the filename separately or possibly both.

My suggestion is to keep the path and filename as separate variables for the maximum flexibility of use. In Picard's scripting, it's easier to combine the two than split them out from a combined value.

Another consideration is how path separators are displayed and saved. The current file naming script preview displays the drive and path as used for the file system (using a colon and backslashes for windows paths). I think this is fine (and appropriate) for the display in the table in the Metadata pane. It's probably okay for the values stored in the variables as well, but we may need to be careful because of the use of the backslashes in paths on windows systems. I assume they would need to be escaped for the tags/variables, although I really don't know the details of how they are saved in the file metadata (if the user chooses to copy the variable to a tag so it is saved).

Motivation: We should try to get this exactly right from the start before we create a backwards compatibility problem for scripters.

I agree, although I don't think we're actually changing any of the existing tags/variables so we shouldn't have any backwards compatibility issues. [Famous last words... 😜]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. The previous path if we have saved the file since it has been loaded.

Do we need this? When the file is saved, I think the file variables are automatically updated to the new path and filename, so I'm not sure what the use case would be for the original path and filename.

I don't personally have a use for this, however this was the initial purpose of this PR.

And we should decide whether to save the entire path (directory path and filename as a string) or the directory path and the filename separately or possibly both.

My suggestion is to keep the path and filename as separate variables for the maximum flexibility of use. In Picard's scripting, it's easier to combine the two than split them out from a combined value.

I agree. Indeed, since we already split out the existing filename and extension, we should probably do that for the proposed filename and extension.

Another consideration is how path separators are displayed and saved. The current file naming script preview displays the drive and path as used for the file system (using a colon and backslashes for windows paths). I think this is fine (and appropriate) for the display in the table in the Metadata pane. It's probably okay for the values stored in the variables as well, but we may need to be careful because of the use of the backslashes in paths on windows systems. I assume they would need to be escaped for the tags/variables, although I really don't know the details of how they are saved in the file metadata (if the user chooses to copy the variable to a tag so it is saved).

Whilst I agree in principle with this, I think that we need to stick with the way ~dirname is set in order to maintain backwards compatibility. Perhaps we should provide a script function to convert local paths to O/S independent paths and back again - and perhaps a path join function to insert the local path separator character ['/', '\']. This should be in a separate PR of course.

I agree, although I don't think we're actually changing any of the existing tags/variables so we shouldn't have any backwards compatibility issues.

I was thinking about not creating future backwards compatibility issues if we later find we got this PR wrong.

@rdswift
Copy link
Collaborator

rdswift commented Nov 24, 2024

If I understand PICARD-2025 correctly, isn't the intent to display the same information that is displayed at the bottom of the File Naming Script Editor window (with the addition of the source path to the source filename), perhaps separated into separate lines for path and filename in the Metadata pane?

image

If so, then I don't believe it's as simple as creating new tags/variables, especially after the files have been saved. It would need to apply the code that calculates the target path and filename to get the values before the files are saved and include them as special lines in the table in the Metadata pane (because if they were tags they would be saved in the resulting files). You could include them as variables in the track metadata (so the user has the option of copying them to a tag for inclusion in the file), and do something special to display those variables in the table. The values for these new variables would need to be updated after every change made to the metadata, including changes made in the table manually. Also, if the variables themselves were used in a tagging script to modify tags/variables used in the file naming script you could end up with a circular reference situation.

My suggestion is to not make the (new) values available in scripts, and add them as separate read-only lines in the table in the Metadata pane. That way the user can benefit from the comparison functionality in the table, but not screw up the metadata or file naming.

EDIT: Actually, if the lines for these new values in the table were read-only, then I think the new variables could be allowed to be copied to tags and saved to the file metadata when the files are saved.

@rdswift
Copy link
Collaborator

rdswift commented Nov 24, 2024

Further to my earlier comment, note that the existing file's path, filename and extension are already available as file metadata variables ~dirname, ~filename and ~extension. Only the new values will need to be calculated, and both old and new displayed in the table. Also, I suggest that they only be displayed if the user has the "Move files when saving" and/or "Rename files when saving" options enabled, otherwise the information will be meaningless. In fact, unless at least one of these options is enabled, there is no need to actually calculate the values. This means that you will also need to track any changes to these option settings and calculate and adjust the display of the metadata table accordingly.

@Sophist-UK
Copy link
Contributor

because if they were tags they would be saved in the resulting files

Yes - I have previously commented that these metadata variables must start with ~ so that they are NOT saved as tags in the file.

The values for these new variables would need to be updated after every change made to the metadata, including changes made in the table manually. Also, if the variables themselves were used in a tagging script to modify tags/variables used in the file naming script you could end up with a circular reference situation.

These are very good points indeed. The possibility of circular references in scripts is an existing possibility (e.g. $set(a, %b%a)$set(b,%a%b)), and but because Tagging scripts run only once after metadata load, and they run sequentially, although there may be a circular case, it does not result in any looping. Similarly the filenaming script runs once when you do a save and doesn't create or change any metadata.

But if we allow the filenaming script to modify metadata implicitly (e.g. ~filename_proposed), then this changes the situation drastically. Without analysing this extensively, I believe that this would only be useful if it is part of an extensive change in script processing whereby to maintain backwards compatibility:

  1. Once any metadata has been downloaded from MusicBrainz, the pre-script new metadata is saved for reuse by scripts.
  2. The filenaming script is run to get the ~*_proposed variables.
  3. The Tagging script is run against the original metadata.
  4. The filenaming script is run again, and the *_proposed variables are compared to the last version. And if any of these are different, then steps 3 and 4 are run again until the values are stable. To prevent infinite loops, this would have to have an upper limit of iterations and give an error if it never stabilises.
  5. We would need to run steps 2. to 4. again every time any metadata changed.
  6. And to prevent any manual edits or changes from manually run scripts being overwritten, we would have to keep track of these and preserve them (and this wouldn't handle the situation where a manually run script might need to be rerun).

Preserving another copy of the metadata for the lifetime of a track/file being loaded seems likely to increase memory usage by 50% (because we will be holding original new in addition to the current original and new. Of course, we could probably save memory by enhancing the metadata class to track the changes to the new and so use memory only for those variables that have changed (either during original automated tagging scripts or manually) - but that would be a major increase in complication.

Consequently, I am forced to come to the same conclusion as Bob, that having new variables for ~directory and ~filename to hold the proposed location after a save is going to need major effort to get it to be useful.

Reluctantly, I think we need to thank Arnab for all his efforts so far, and (if he is willing) ask him to start afresh and focus on making the proposed path and filename visible in the Metadata pane and NOT to create new hidden ~* metadata from the filenaming script.

(@zytact - Let me be the first to apologise for my lack of analytical forethought being part of having caused you to waste time on the current false start. If others agree with this analysis, then... if you still wish to create new metadata variables for your own use or for minority use, then you will need to use a plugin to do it rather than have it as part of Picard.)

@zas zas requested a review from phw November 25, 2024 15:27
Copy link
Member

@phw phw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for trying to tackle this. I think this change as it is misses the point and is too simplistic, though.

It introduces path_old, which is only available after saving. The new path is then the current path. So with this patch you have the current path on the right and any path before saving on the left.

What would actually be needed is showing the current path on the left, and having the new path as it is expected to be after saving shown on the right. This is the tricky part and requires running the renaming script on the metadata. In the ticket I also mentioned how this can be a performance issue. But if this is only done for single file selection on display this might be fine.

Also in order to show an entry in the tag list it would not be appropriate to modify the file.metadata or file.orig_metadata, as this will create tags or variables. Another solution needs to be found to add this information to the data displayed in metadatabox.py.

@zytact
Copy link
Contributor Author

zytact commented Nov 26, 2024

Thank you so much for taking the time to review the pull request I submitted. I truly appreciate the effort and feedback you've already provided.

(Let me be the first to apologize for my lack of analytical forethought being part of having caused you to waste time on the current false start. If others agree with this analysis, then... if you still wish to create new metadata variables for your own use or for minority use, then you will need to use a plugin to do it rather than have it as part of Picard.)

Thank you for saying that @Sophist-UK . Honestly, that is alright, it happens.

I wanted to let you all know that looking at the feedback you all provided, I had underestimated how much time this would require, as I am currently in the middle of semester exams. Unfortunately, my schedule has been much busier than anticipated. My exams will conclude in about four weeks, and I will be able to dedicate more focused time to addressing the remaining changes and feedback after that.

I hope this delay isn’t too inconvenient, and I sincerely apologize. Thank you for your understanding, and I’ll make sure to follow up as soon as I’m able.

@Sophist-UK
Copy link
Contributor

Since the tickets requesting this date back to 2019/2020, I don't think a few weeks delay for you to complete your exams will be a major problem for anyone. 😃 And relatively minor Picard enhancements have got to come a long way down anyone's priority list compared to exams that will decide your future life!!

@phw
Copy link
Member

phw commented Nov 26, 2024

I hope this delay isn’t too inconvenient, and I sincerely apologize. Thank you for your understanding, and I’ll make sure to follow up as soon as I’m able.

Quite the opposite, if you are interested in making the necessary changes you are more then welcome and I think we will all be happy to guide you through the task. This is really one of those issues that should be solved.

That it is more complex than it looks like on first sight is the main reason why this ticket is still open. On the other hand I think it is also not as complicated as we maybe first thought when looking at the ticket.

@zytact
Copy link
Contributor Author

zytact commented Nov 26, 2024

Thank you both for your kind words. I'm definitely still interested in working on it and greatly appreciate your willingness to guide me through the process. I'll make sure to revisit it with fresh focus once my exams are over! 😊

@rdswift
Copy link
Collaborator

rdswift commented Nov 27, 2024

@zytact,

When you pick this up again, here are a couple of links to existing code that may be of interest:

Function to produce the before and after file naming example tuple for the specified file (used in the File Naming Script Editor to display examples):

def _example_to_filename(self, file):

The metadata table widget (where you would be adding the before and after path and filename items for display):

class MetadataBox(QtWidgets.QTableWidget):

@zytact
Copy link
Contributor Author

zytact commented Dec 1, 2024

Thank you, I'll be sure to check these out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants