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

Episode path format #108

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 38 additions & 12 deletions client/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
img{
display: none;
}
#pathFormatTable td.key { font-weight: bold; }
#pathFormatTable td.description { font-weight: normal; }
@media (max-width: 750px) {
.label-body{
display: inline!important;
Expand All @@ -26,6 +28,17 @@

}
</style>
<script type="text/javascript">
function insertText(elemID, text){
var elem = document.getElementById(elemID)
elem.value += text;
elem.focus();
elem.setSelectionRange(
elem.value.length,
elem.value.length
);
}
</script>
</head>
<body>
<div class="container">
Expand Down Expand Up @@ -74,14 +87,29 @@ <h3>Settings</h3>
<span class="label-body">Automatically download new episodes to the disk</span>
</label>

<label for="appendDateToFileName">
<input type="checkbox" name="appendDateToFileName" v-model="appendDateToFileName">
<span class="label-body">Append episode date to episode file name</span>
</label>

<label for="appendEpisodeNumberToFileName">
<input type="checkbox" name="appendEpisodeNumberToFileName" v-model="appendEpisodeNumberToFileName">
<span class="label-body">Append episode number to episode file name</span>
<label for="fileNameFormat">
<span class="label-body">Episode file name format:</span>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the text field where the format is entered. Ideally this would be less direct and populated by dragging tokens in to the field or by selection from a menu, but it's a start at least.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a bit of Javascript to help populate the format field when the format keys are clicked.

<input type="text" name="fileNameFormat" id="fileNameFormatInput" v-model="fileNameFormat" width="100%">
<table id="pathFormatTable">
<thead>
<tr class="header">
<th class="key">Format Key</th>
<th class="description">Description</th>
</tr>
</thead>
<tbody>
<tr><td class="description" colspan=2>Click the key to add it to the format field.</td></tr>
<tr><td class="key"><a onclick="insertText('fileNameFormatInput', '%YYYY%');">%YYYY%</a></td><td class="description">Episode published year (ie: 1999)</td></tr>
<tr><td class="key"><a onclick="insertText('fileNameFormatInput', '%mm%');">%mm%</a></td><td class="description">Episode published month (ie: 12)</td></tr>
<tr><td class="key"><a onclick="insertText('fileNameFormatInput', '%dd%');">%dd%</a></td><td class="description">Episode published date (ie: 31)</td></tr>
<tr><td class="key"><a onclick="insertText('fileNameFormatInput', '%EpisodeTitle%');">%EpisodeTitle%</a></td><td class="description">Episode title</td></tr>
<tr><td class="key"><a onclick="insertText('fileNameFormatInput', '%EpisodeNumber%');">%EpisodeNumber%</a></td><td class="description">Episode number<br />(specify the minimum number of digits (ie. 3) as %EpisodeNumber:3%)</td></tr>
<tr><td class="key"><a onclick="insertText('fileNameFormatInput', '%EpisodeDate%');">%EpisodeDate%</a></td><td class="description">Episode date (ie: 1999-12-31)</td></tr>
<tr><td class="key"><a onclick="insertText('fileNameFormatInput', '%ShowTitle%');">%ShowTitle%</a></td><td class="description">Show title</td></tr>
<tr><td class="key"><a onclick="insertText('fileNameFormatInput', '%%');">%%</a></td><td class="description">Literal '%'</td></tr>
<tr><td class="key"><a onclick="insertText('fileNameFormatInput', '/');">/</a></td><td class="description">Path separator</td></tr>
</tbody>
</table>
</label>

<label for="darkMode">
Expand Down Expand Up @@ -182,8 +210,7 @@ <h3>More Info</h3>
downloadOnAdd: self.downloadOnAdd,
initialDownloadCount:self.initialDownloadCount,
autoDownload:self.autoDownload,
appendDateToFileName:self.appendDateToFileName,
appendEpisodeNumberToFileName:self.appendEpisodeNumberToFileName,
fileNameFormat:self.fileNameFormat,
darkMode:self.darkMode,
downloadEpisodeImages:self.downloadEpisodeImages,
generateNFOFile:self.generateNFOFile,
Expand Down Expand Up @@ -227,8 +254,7 @@ <h3>More Info</h3>
downloadOnAdd: {{ .setting.DownloadOnAdd }},
initialDownloadCount: {{ .setting.InitialDownloadCount }},
autoDownload: {{ .setting.AutoDownload }},
appendDateToFileName: {{ .setting.AppendDateToFileName }},
appendEpisodeNumberToFileName:{{ .setting.AppendEpisodeNumberToFileName }},
fileNameFormat: {{ .setting.FileNameFormat }},
darkMode:{{ .setting.DarkMode }},
originalThemeSetting:{{ .setting.DarkMode }},
downloadEpisodeImages:{{.setting.DownloadEpisodeImages }},
Expand Down
3 changes: 1 addition & 2 deletions controllers/pages.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ type SettingModel struct {
DownloadOnAdd bool `form:"downloadOnAdd" json:"downloadOnAdd" query:"downloadOnAdd"`
InitialDownloadCount int `form:"initialDownloadCount" json:"initialDownloadCount" query:"initialDownloadCount"`
AutoDownload bool `form:"autoDownload" json:"autoDownload" query:"autoDownload"`
AppendDateToFileName bool `form:"appendDateToFileName" json:"appendDateToFileName" query:"appendDateToFileName"`
AppendEpisodeNumberToFileName bool `form:"appendEpisodeNumberToFileName" json:"appendEpisodeNumberToFileName" query:"appendEpisodeNumberToFileName"`
FileNameFormat string `form:"fileNameFormat" json:"fileNameFormat" query:"fileNameFormat"`
DarkMode bool `form:"darkMode" json:"darkMode" query:"darkMode"`
DownloadEpisodeImages bool `form:"downloadEpisodeImages" json:"downloadEpisodeImages" query:"downloadEpisodeImages"`
GenerateNFOFile bool `form:"generateNFOFile" json:"generateNFOFile" query:"generateNFOFile"`
Expand Down
16 changes: 12 additions & 4 deletions controllers/podcast.go
Original file line number Diff line number Diff line change
Expand Up @@ -626,10 +626,18 @@ func UpdateSetting(c *gin.Context) {

if err == nil {

err = service.UpdateSettings(model.DownloadOnAdd, model.InitialDownloadCount,
model.AutoDownload, model.AppendDateToFileName, model.AppendEpisodeNumberToFileName,
model.DarkMode, model.DownloadEpisodeImages, model.GenerateNFOFile, model.DontDownloadDeletedFromDisk, model.BaseUrl,
model.MaxDownloadConcurrency, model.UserAgent,
err = service.UpdateSettings(
model.DownloadOnAdd,
model.InitialDownloadCount,
model.AutoDownload,
model.FileNameFormat,
model.DarkMode,
model.DownloadEpisodeImages,
model.GenerateNFOFile,
model.DontDownloadDeletedFromDisk,
model.BaseUrl,
model.MaxDownloadConcurrency,
model.UserAgent,
)
if err == nil {
c.JSON(200, gin.H{"message": "Success"})
Expand Down
63 changes: 50 additions & 13 deletions db/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,71 @@ import (
)

type localMigration struct {
Name string
Query string
Name string
Condition []string
Query []string
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a Condition string array and turned Query in to a string array to support skipping a migration if it's not necessary, and to support multiple Condition and Query statements.

}

var migrations = []localMigration{
{
Name: "2020_11_03_04_42_SetDefaultDownloadStatus",
Query: "update podcast_items set download_status=2 where download_path!='' and download_status=0",
Name: "2020_11_03_04_42_SetDefaultDownloadStatus",
Condition: []string{},
Query: []string{
"update podcast_items set download_status=2 where download_path!='' and download_status=0",
},
},
{
Name: "2021_06_01_00_00_ConvertFileNameFormat",
Condition: []string{
"SELECT COUNT(*) > 0 FROM (SELECT name FROM pragma_table_info('settings') where name is 'append_date_to_file_name');",
"SELECT COUNT(*) > 0 FROM (SELECT name FROM pragma_table_info('settings') where name is 'append_episode_number_to_file_name');",
},
Query: []string{
"UPDATE settings SET file_name_format = CASE WHEN append_date_to_file_name AND append_episode_number_to_file_name THEN '%EpisodeNumber%-%EpisodeDate%-%EpisodeTitle%' WHEN append_date_to_file_name THEN '%EpisodeDate%-%EpisodeTitle%' WHEN append_episode_number_to_file_name THEN '%EpisodeNumber%-%EpisodeTitle%' ELSE '%EpisodeTitle%' END",
// sqlite3 v3.35.0 supports DROP COLUMN
// "ALTER TABLE settings DROP COLUMN append_episode_number_to_file_name",
// "ALTER TABLE settings DROP COLUMN append_date_to_file_name",
},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

ConvertFileNameFormat needs conditions to ensure it doesn't try to convert the old "append" fields to the new "file name format" if those fields don't exist in the database. Ideally multiple Query statements would be used to drop the old "append" columns, but that's not supported until sqlite3 v3.35. The alternative is recreating the table, but that seems excessive.
Are there other ways to accomplish this?

},
}

func RunMigrations() {
for _, mig := range migrations {
ExecuteAndSaveMigration(mig.Name, mig.Query)
fmt.Println(mig.Name)
ExecuteAndSaveMigration(mig.Name, mig.Condition, mig.Query)
}
}
func ExecuteAndSaveMigration(name string, query string) error {
func ExecuteAndSaveMigration(name string, condition []string, query []string) error {
var migration Migration
result := DB.Where("name=?", name).First(&migration)
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
fmt.Println(query)
result = DB.Debug().Exec(query)
if result.Error == nil {
DB.Save(&Migration{
Date: time.Now(),
Name: name,
})
var rawResult string
var shouldMigrate = true
for _, q := range condition {
fmt.Println("cond: " + q)
result = DB.Debug().Raw(q).Scan(&rawResult)
if result.Error != nil {
fmt.Println(result.Error)
return result.Error
}
shouldMigrate = shouldMigrate && rawResult == "1"
}
if shouldMigrate {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Only execute the migration if the Conditions succeeded.

for _, q := range query {
fmt.Println("exec: " + q)
result = DB.Debug().Exec(q)
if result.Error != nil {
fmt.Println(result.Error)
return result.Error
}
}
} else {
fmt.Println("migration not required")
}
DB.Save(&Migration{
Date: time.Now(),
Name: name,
})
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Still save the migration to the Migration table as long as there wasn't an actual error.

return result.Error
}
return nil
Expand Down
3 changes: 1 addition & 2 deletions db/podcast.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,7 @@ type Setting struct {
DownloadOnAdd bool `gorm:"default:true"`
InitialDownloadCount int `gorm:"default:5"`
AutoDownload bool `gorm:"default:true"`
AppendDateToFileName bool `gorm:"default:false"`
AppendEpisodeNumberToFileName bool `gorm:"default:false"`
FileNameFormat string `gorm:"default:%EpisodeTitle%"`
DarkMode bool `gorm:"default:false"`
DownloadEpisodeImages bool `gorm:"default:false"`
GenerateNFOFile bool `gorm:"default:false"`
Expand Down
28 changes: 16 additions & 12 deletions service/fileService.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
stringy "github.com/gobeam/stringy"
)

func Download(link string, episodeTitle string, podcastName string, prefix string) (string, error) {
func Download(link string, episodeTitle string, podcastName string, episodePathName string) (string, error) {
if link == "" {
return "", errors.New("Download path empty")
}
Expand All @@ -39,12 +39,13 @@ func Download(link string, episodeTitle string, podcastName string, prefix strin
return "", err
}

fileName := getFileName(link, episodeTitle, ".mp3")
if prefix != "" {
fileName = fmt.Sprintf("%s-%s", prefix, fileName)
}
folder := createDataFolderIfNotExists(podcastName)
finalPath := path.Join(folder, fileName)
fileExtension := path.Ext(getFileName(link, episodeTitle, ".mp3"))
finalPath := path.Join(
os.Getenv("DATA"),
cleanFileName(podcastName),
fmt.Sprintf("%s%s", episodePathName, fileExtension))
dir, _ := path.Split(finalPath)
createPreSanitizedPath(dir)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mostly use the episodePathName as it is passed in to Download() and don't sanitize the path components again as they were already sanitized as they were created.


if _, err := os.Stat(finalPath); !os.IsNotExist(err) {
changeOwnership(finalPath)
Expand Down Expand Up @@ -358,17 +359,21 @@ func getRequest(url string) (*http.Request, error) {
return req, nil
}

func createFolder(folder string, parent string) string {
folder = cleanFileName(folder)
//str := stringy.New(folder)
folderPath := path.Join(parent, folder)
func createPreSanitizedPath(folderPath string) string {
if _, err := os.Stat(folderPath); os.IsNotExist(err) {
os.MkdirAll(folderPath, 0777)
changeOwnership(folderPath)
}
return folderPath
}

func createFolder(folder string, parent string) string {
folder = cleanFileName(folder)
//str := stringy.New(folder)
folderPath := path.Join(parent, folder)
return createPreSanitizedPath(folderPath)
}

func createDataFolderIfNotExists(folder string) string {
dataPath := os.Getenv("DATA")
return createFolder(folder, dataPath)
Expand All @@ -395,7 +400,6 @@ func getFileName(link string, title string, defaultExtension string) string {
//str := stringy.New(title)
str := stringy.New(cleanFileName(title))
return str.KebabCase().Get() + ext

}

func cleanFileName(original string) string {
Expand Down
Loading