FileManager.vue 47.3 KB
Newer Older
1001
        headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token },
1002
1003
      }).then(() => {
        this.$log.debug(`Fetching files for show ${this.shows[this.currentShow].slug} (ID: ${id})`)
1004
        this.fetchFiles(this.shows[this.currentShow].slug)
1005
1006
1007
1008
      }).catch(error => {
        // if there was a 409 Conflict response it means, that this file is
        // still used in one or more playlists.
        if (error.response.status === 409) {
1009
          let pls = error.response.data.detail.playlists.length
1010
          let msg = 'Cannot delete file. Still used in ' + pls + ' playlists:\n\n'
1011
          for (let pl of error.response.data.detail.playlists) {
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
            msg += 'ID: ' + pl.id
            if (pl.description) {
              msg += ' (' + pl.description + ')'
            }
            msg += '\n'
          }
          msg += '\nIf you want to delete the file, remove it from those playlists first.'
          alert(msg)
        } else {
          this.$log.error(error.response.status + ' ' + error.response.statusText)
          this.$log.error(error.response)
          alert('Error: could not delete file. See console for details.')
        }
1025
1026
      })
    },
1027
1028
1029
1030
1031
1032

    // With this function we add a new file in the AuRa tank by calling its API.
    // Depending on wheter we add a remote file which tank then imports by itself,
    // or if we want to upload a local file, we source-uri has to look different.
    // And for uploading a local file this is just the first step. Afterwards the
    // actual upload has to be started with the startUpload function.
1033
    addFile: function () {
1034
1035
      var uri = process.env.VUE_APP_API_TANK + 'shows/' + this.shows[this.currentShow].slug + '/files'
      if (this.addNewFileURI) {
1036
1037
1038
1039
        // TODO: add mechanism to indicate the running post request in the files table
        axios.post(uri, { 'source-uri': this.uploadSourceURI }, {
          withCredentials: true,
          headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
1040
        }).then(() => {
1041
          this.fetchFiles(this.shows[this.currentShow].slug)
1042
1043
1044
1045
          if (this.uploadInterval === null) {
            this.uploadInterval = setInterval(() => { this.fetchImports(this.shows[this.currentShow].slug) }, 250)
          }
        }).catch(error => {
1046
1047
          this.$log.error(error.response.status + ' ' + error.response.statusText)
          this.$log.error(error.response)
1048
          alert('Error: could not add the new remote import. See console for details.')
1049
        })
1050
      } else if (this.uploadSourceFile) {
1051
        // TODO: add mechanism to indicate the running post request in the files table
1052
        axios.post(uri, { 'source-uri': encodeURI(encodeURI('upload://' + this.uploadSourceFile.name)) }, {
1053
1054
1055
1056
          withCredentials: true,
          headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
        }).then(response => {
          this.startUpload(response.data.id)
1057
          this.fetchFiles(this.shows[this.currentShow].slug)
1058
1059
1060
          if (this.uploadInterval === null) {
            this.uploadInterval = setInterval(() => { this.fetchImports(this.shows[this.currentShow].slug) }, 250)
          }
1061
        }).catch(error => {
1062
1063
          this.$log.error(error.response.status + ' ' + error.response.statusText)
          this.$log.error(error.response)
1064
          alert('Error: could not add the new file upload. See console for details.')
1065
1066
        })
      } else {
1067
        alert('Something is wrong. You have choosen to upload a file, but the corresponding file object does not exist.')
1068
1069
      }
    },
1070
1071
1072
1073

    // When a new file was added with the addFile function we can start an upload
    // fetching the import endpoint of this file and then call the upload
    // function, which atually puts the file onto the server.
1074
1075
1076
1077
1078
1079
1080
1081
1082
    startUpload: function (id) {
      var uri = process.env.VUE_APP_API_TANK + 'shows/' + this.shows[this.currentShow].slug + '/files/' + id + '/import'
      axios.get(uri, {
        withCredentials: true,
        headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token },
        params: {'wait-for': 'running'}
      }).then(
        this.upload(id)
      ).catch(error => {
1083
1084
        this.$log.error(error.response.status + ' ' + error.response.statusText)
        this.$log.error(error.response)
1085
        alert('Error: could not start the file upload. See console for details.')
1086
1087
      })
    },
1088
1089
1090

    // Upload a file to the AuRa tank API - given it was created with the addFile
    // and started with the startUpload methods.
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
    upload: function (id) {
      /*
       * NOTE: there is no npm package for flow.js and importing it manually did not
       *       work so far. therefore this is commented out and we are using the simple
       *       upload method, until there is a nice npm package for flow.js or somone
       *       resolves this issue otherwise
      var flow = new Flow({
        target: process.env.VUE_APP_API_TANK + 'shows/' + this.shows[this.currentShow].slug + '/files/' + id + '/upload',
        chunkSize: 100 * 1024,
        prioritizeFirstAndLastChunk: true
      })
      flow.on('fileSuccess', function(file, message) {
1103
        this.$log.error(file, message)
1104
1105
      })
      flow.on('fileError', function(file, message) {
1106
        this.$log.error(file, message)
1107
        alert('Error: could not upload your file. See console for details.')
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
      })
      flow.addFile(this.uploadSourceFile)
      flow.upload()
      */
      var uri = process.env.VUE_APP_API_TANK + 'shows/' + this.shows[this.currentShow].slug + '/files/' + id + '/upload'
      axios.put(uri, this.uploadSourceFile, {
        withCredentials: true,
        headers: {
          'Authorization': 'Bearer ' + this.$parent.user.access_token,
          'Content-Type': 'application/octet-stream'
        }
1119
      }).then(() => {
1120
        this.$log.info('Sucessfully uploaded file.')
1121
1122
1123
1124
1125
        // now we start polling for the import progress
        // the fetchImports function has to make sure to deactivate the interval
        // again, when all running imports are done (in this first raw version;
        // ideally we should refine this so that every single file gets updated independently)
        //this.uploadInterval = setInterval(() => { this.fetchImports(this.shows[this.currentShow].slug) }, 100)
1126
      }).catch(error => {
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
        if (error.response.status === 500 && error.response.data.error === 'ffmpeg returned 1') {
          this.$log.error(error.response.status + ' ' + error.response.statusText)
          this.$log.error(error.response)
          // if we use a file format that is not supported by ffmpeg, we should find
          // the second to last line should notify us about invalid data
          let ffmpegError = error.response.data.detail[error.response.data.detail.length - 2]
          if (ffmpegError.line === 'pipe:: Invalid data found when processing input') {
            // in this case we can make the error message in the files table more specific
            alert('Error: import aborted. The audio data format of your file is not valid!')
          } else {
            alert('Error: ffmpeg could not processs your file! See console for details.')
          }
        } else {
          this.$log.error(error.response.status + ' ' + error.response.statusText)
          this.$log.error(error.response)
          alert('Error: could not finish the file upload/import. See console for details.')
        }
1144
      })
1145
    },
1146
1147
1148

    // This switches the UI elements to reflect another show and fetches all
    // relevent data from the tank API.
1149
1150
1151
1152
    switchShow: function (index) {
      // set the current show and its ID to whatever we want to switch to now
      this.currentShow = index
      this.currentShowID = this.shows[this.currentShow].id
1153
      this.fetchShow(this.shows[this.currentShow].slug)
1154
      this.$log.debug('show switched to ' + this.shows[this.currentShow].slug + ' (id: ' + this.shows[this.currentShow].id + ')')
1155
    },
1156
1157
1158

    // This function is used to visually switch between the files and playlists
    // editing mode.
1159
1160
1161
1162
    switchMode: function (mode) {
      if (this.mode !== mode) {
        this.mode = mode
        for (var b in this.button) {
1163
1164
          if (b === mode) { this.button[b] = 'info' }
          else { this.button[b] = 'outline-info' }
1165
1166
1167
        }
      }
    },
1168
1169
1170
1171
1172

    // This function fetches all running imports for a given show. It should
    // be called periodically to reflect the upload/import progress. When no
    // more active imports are available the corresponding updateInterval
    // should be cleared again.
1173
1174
1175
1176
1177
1178
1179
    fetchImports: function (slug){
      var uri = process.env.VUE_APP_API_TANK + 'shows/' + slug + '/imports'
      axios.get(uri, {
        withCredentials: true,
        headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
      }).then(response => {
        // if all imports are done, we will receive an empty result set and stop
1180
1181
        // polling the server again. then we can also refetch all file statuses.
        if (response.data.results.length === 0) {
1182
1183
1184
          clearInterval(this.uploadInterval)
          this.uploadInterval = null
          this.fetchShow(slug)
1185
1186
1187
1188
1189
        }
        // if there are still imports going on, we just updated the respective
        // progress information for the import we just fetched and rerender
        // the filesTable
        else {
1190
1191
1192
1193
1194
1195
1196
          for (var i in response.data.results) {
            var f = this.getFileById(response.data.results[i].id)
            if (f) {
              f.source.import.progress = {
                progress: response.data.results[i].progress.progress,
                step: response.data.results[i].progress.step
              }
1197
1198
1199
1200
1201
              let p = '[import]'
              p += ' id: ' + response.data.results[i].id
              p += ', progress: ' + response.data.results[i].progress.progress
              p += ', step: ' + response.data.results[i].progress.step
              this.$log.debug(p)
1202
1203
1204
1205
1206
            }
            this.$refs.filesTable.refresh()
          }
        }
      }).catch(error => {
1207
1208
1209
1210
1211
1212
1213
1214
1215
        // also in case of an error we have to cancel the uploadInterval,
        // otherwise we'll continuosly get alerts if the error is persistent
        clearInterval(this.uploadInterval)
        if (error.response) {
          this.$log.error(error.response.status + ' ' + error.response.statusText)
          this.$log.error(error.response)
        } else {
          this.$log.error(error)
        }
1216
        alert('Error: could not fetch current imports. See console for details.')
1217
1218
      })
    },
1219
1220

    // Just a wrapper to fetch both, files and playlists information.
1221
1222
1223
1224
    fetchShow: function (slug) {
      this.fetchFiles(slug)
      this.fetchPlaylists(slug)
    },
1225
1226

    // Fetch all files for a given show from the AuRa tank API
1227
    fetchFiles: function (slug) {
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
      this.loaded.files = false
      var uri = process.env.VUE_APP_API_TANK + 'shows/' + slug + '/files'
      axios.get(uri, {
        withCredentials: true,
        headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
      }).then(response => {
        // we don't have to check separately, if there are files, because tank
        // always provides an empty array if there are no files (or even if there is no corresponding show)
        this.files = response.data.results
        this.loaded.files = true
      }).catch(error => {
1239
1240
        this.$log.error(error.response.status + ' ' + error.response.statusText)
        this.$log.error(error.response)
1241
        alert('Error: could not fetch files from tank. See console for details.')
1242
      })
1243
    },
1244
1245

    // Fetch all playlists for a given show from the AuRa tank API
1246
1247
1248
    fetchPlaylists: function (slug) {
      this.loaded.playlists = false
      var uri = process.env.VUE_APP_API_TANK + 'shows/' + slug + '/playlists'
1249
1250
1251
1252
1253
1254
1255
1256
1257
      axios.get(uri, {
        withCredentials: true,
        headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
      }).then(response => {
        // we don't have to check separately, if there are playlists, because tank
        // always provides an empty array if there are no playlists (or even if there is no corresponding show)
        this.playlists = response.data.results
        this.loaded.playlists = true
      }).catch(error => {
1258
1259
        this.$log.error(error.response.status + ' ' + error.response.statusText)
        this.$log.error(error.response)
1260
        alert('Error: could not fetch playlists from tank. See console for details.')
1261
      })
1262
1263
1264
    }
  }
}
1265
1266
1267
</script>

<style>
1268
1269
1270
1271
1272
div.filelistbox {
  border: 1px solid #e9ecef;
  border-radius: 0.3rem;
  padding: 1rem 2rem;
}
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
.stateNew {
  color: red;
  font-weight: bold;
}
.stateRunning {
  color: darkgreen;
}
.stateUndefined {
  color: orange;
  font-weight: bold;
}
1284
1285
1286
1287
1288
1289
.upDownArrows {
  font-size: 1.15rem;
}
.modalPlaylistRows {
  padding: 0.2rem 0;
}
1290
</style>