mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-03-08 04:20:08 +03:00
Compare commits
29 Commits
v4.2
...
categories
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6481102e01 | ||
|
|
d7d861ef0e | ||
|
|
1d5490c0ff | ||
|
|
28ee77cee0 | ||
|
|
133d848729 | ||
|
|
a78f4e99d0 | ||
|
|
539bc5094a | ||
|
|
f0f2faa398 | ||
|
|
7835185fe0 | ||
|
|
95bb69f16b | ||
|
|
a93aa080b3 | ||
|
|
ed1375d40b | ||
|
|
db78e4ad5e | ||
|
|
6ef0082563 | ||
|
|
b978007472 | ||
|
|
c09dd7a03b | ||
|
|
b6c09324d9 | ||
|
|
1a900399d8 | ||
|
|
ea959547fd | ||
|
|
085849c7ee | ||
|
|
cf1dd43d36 | ||
|
|
250f150587 | ||
|
|
dbf08e1276 | ||
|
|
f74ce4b865 | ||
|
|
8e4e0c7908 | ||
|
|
b0cb09309d | ||
|
|
75c1c9e9b7 | ||
|
|
c19e0bb881 | ||
|
|
a1af5496c7 |
11
.github/workflows/build.yml
vendored
11
.github/workflows/build.yml
vendored
@@ -69,17 +69,20 @@ jobs:
|
||||
with:
|
||||
name: youtubedl-material
|
||||
path: ${{runner.temp}}/youtubedl-material
|
||||
- name: extract tag name
|
||||
id: tag_name
|
||||
run: echo ::set-output name=tag_name::${GITHUB_REF#refs/tags/}
|
||||
- name: prepare release asset
|
||||
shell: pwsh
|
||||
run: Compress-Archive -Path ${{runner.temp}}/youtubedl-material -DestinationPath youtubedl-material-${{ github.ref }}.zip
|
||||
- name: upload build asset
|
||||
run: Compress-Archive -Path ${{runner.temp}}/youtubedl-material -DestinationPath youtubedl-material-${{ steps.tag_name.outputs.tag_name }}.zip
|
||||
- name: upload release asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./youtubedl-material-${{ github.ref }}.zip
|
||||
asset_name: youtubedl-material-${{ github.ref }}.zip
|
||||
asset_path: ./youtubedl-material-${{ steps.tag_name.outputs.tag_name }}.zip
|
||||
asset_name: youtubedl-material-${{ steps.tag_name.outputs.tag_name }}.zip
|
||||
asset_content_type: application/zip
|
||||
- name: upload docker-compose asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
|
||||
32
.github/workflows/docker-release.yml
vendored
Normal file
32
.github/workflows/docker-release.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: docker-release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tags:
|
||||
description: 'Docker tags'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: setup platform emulator
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- name: setup multi-arch docker build
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: build & push images
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm,linux/arm64/v8
|
||||
push: true
|
||||
tags: ${{ github.event.inputs.tags }}
|
||||
@@ -16,15 +16,11 @@ Check out the prerequisites, and go to the installation section. Easy as pie!
|
||||
|
||||
Here's an image of what it'll look like once you're done:
|
||||
|
||||

|
||||
|
||||
With optional file management enabled (default):
|
||||
|
||||

|
||||
<img src="https://i.imgur.com/C6vFGbL.png" width="800">
|
||||
|
||||
Dark mode:
|
||||
|
||||

|
||||
<img src="https://i.imgur.com/vOtvH5w.png" width="800">
|
||||
|
||||
### Prerequisites
|
||||
|
||||
|
||||
@@ -1124,6 +1124,8 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
|
||||
const download = downloads[session][download_uid];
|
||||
updateDownloads();
|
||||
|
||||
let download_checker = null;
|
||||
|
||||
// get video info prior to download
|
||||
let info = await getVideoInfoByURL(url, downloadConfig, download);
|
||||
if (!info && url.includes('youtu')) {
|
||||
@@ -1131,7 +1133,7 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
|
||||
return;
|
||||
} else if (info) {
|
||||
// check if it fits into a category. If so, then get info again using new downloadConfig
|
||||
category = await categories_api.categorize(info);
|
||||
if (!Array.isArray(info) || config_api.getConfigItem('ytdl_allow_playlist_categorization')) category = await categories_api.categorize(info);
|
||||
|
||||
// set custom output if the category has one and re-retrieve info so the download manager has the right file name
|
||||
if (category && category['custom_output']) {
|
||||
@@ -1142,15 +1144,19 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
|
||||
}
|
||||
|
||||
// store info in download for future use
|
||||
download['_filename'] = info['_filename'];
|
||||
if (Array.isArray(info)) {
|
||||
download['fileNames'] = [];
|
||||
for (let info_obj of info) download['fileNames'].push(info_obj['_filename']);
|
||||
} else {
|
||||
download['_filename'] = info['_filename'];
|
||||
}
|
||||
download['filesize'] = utils.getExpectedFileSize(info);
|
||||
download_checker = setInterval(() => checkDownloadPercent(download), 1000);
|
||||
}
|
||||
|
||||
const download_checker = setInterval(() => checkDownloadPercent(download), 1000);
|
||||
|
||||
// download file
|
||||
youtubedl.exec(url, downloadConfig, {}, function(err, output) {
|
||||
clearInterval(download_checker); // stops the download checker from running as the download finished (or errored)
|
||||
if (download_checker) clearInterval(download_checker); // stops the download checker from running as the download finished (or errored)
|
||||
|
||||
download['downloading'] = false;
|
||||
download['timestamp_end'] = Date.now();
|
||||
@@ -1188,7 +1194,7 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
|
||||
}
|
||||
|
||||
// get filepath with no extension
|
||||
const filepath_no_extension = removeFileExtension(output_json['_filename']);
|
||||
const filepath_no_extension = utils.removeFileExtension(output_json['_filename']);
|
||||
|
||||
var full_file_path = filepath_no_extension + ext;
|
||||
var file_name = filepath_no_extension.substring(fileFolderPath.length, filepath_no_extension.length);
|
||||
@@ -1214,7 +1220,7 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
|
||||
title: output_json['title'],
|
||||
artist: output_json['artist'] ? output_json['artist'] : output_json['uploader']
|
||||
}
|
||||
let success = NodeID3.write(tags, output_json['_filename']);
|
||||
let success = NodeID3.write(tags, utils.removeFileExtension(output_json['_filename']) + '.mp3');
|
||||
if (!success) logger.error('Failed to apply ID3 tag to audio file ' + output_json['_filename']);
|
||||
}
|
||||
|
||||
@@ -1310,7 +1316,7 @@ async function downloadFileByURL_normal(url, type, options, sessionID = null) {
|
||||
video.on('info', function(info) {
|
||||
video_info = info;
|
||||
file_size = video_info.size;
|
||||
const json_path = removeFileExtension(video_info._filename) + '.info.json';
|
||||
const json_path = utils.removeFileExtension(video_info._filename) + '.info.json';
|
||||
fs.ensureFileSync(json_path);
|
||||
fs.writeJSONSync(json_path, video_info);
|
||||
video.pipe(fs.createWriteStream(video_info._filename, { flags: 'w' }))
|
||||
@@ -1336,14 +1342,14 @@ async function downloadFileByURL_normal(url, type, options, sessionID = null) {
|
||||
let difference = (new_date - date)/1000;
|
||||
logger.debug(`Video download delay: ${difference} seconds.`);
|
||||
download['timestamp_end'] = Date.now();
|
||||
download['fileNames'] = [removeFileExtension(video_info._filename) + ext];
|
||||
download['fileNames'] = [utils.removeFileExtension(video_info._filename) + ext];
|
||||
download['complete'] = true;
|
||||
updateDownloads();
|
||||
|
||||
// audio-only cleanup
|
||||
if (is_audio) {
|
||||
// filename fix
|
||||
video_info['_filename'] = removeFileExtension(video_info['_filename']) + '.mp3';
|
||||
video_info['_filename'] = utils.removeFileExtension(video_info['_filename']) + '.mp3';
|
||||
|
||||
// ID3 tagging
|
||||
let tags = {
|
||||
@@ -1353,8 +1359,8 @@ async function downloadFileByURL_normal(url, type, options, sessionID = null) {
|
||||
let success = NodeID3.write(tags, video_info._filename);
|
||||
if (!success) logger.error('Failed to apply ID3 tag to audio file ' + video_info._filename);
|
||||
|
||||
const possible_webm_path = removeFileExtension(video_info['_filename']) + '.webm';
|
||||
const possible_mp4_path = removeFileExtension(video_info['_filename']) + '.mp4';
|
||||
const possible_webm_path = utils.removeFileExtension(video_info['_filename']) + '.webm';
|
||||
const possible_mp4_path = utils.removeFileExtension(video_info['_filename']) + '.mp4';
|
||||
// check if audio file is webm
|
||||
if (fs.existsSync(possible_webm_path)) await convertFileToMp3(possible_webm_path, video_info['_filename']);
|
||||
else if (fs.existsSync(possible_mp4_path)) await convertFileToMp3(possible_mp4_path, video_info['_filename']);
|
||||
@@ -1371,7 +1377,7 @@ async function downloadFileByURL_normal(url, type, options, sessionID = null) {
|
||||
fs.appendFileSync(archive_path, diff);
|
||||
}
|
||||
|
||||
videopathEncoded = encodeURIComponent(removeFileExtension(base_file_name));
|
||||
videopathEncoded = encodeURIComponent(utils.removeFileExtension(base_file_name));
|
||||
|
||||
resolve({
|
||||
[is_audio ? 'audiopathEncoded' : 'videopathEncoded']: videopathEncoded,
|
||||
@@ -1534,6 +1540,9 @@ async function getVideoInfoByURL(url, args = [], download = null) {
|
||||
resolve(output);
|
||||
} else {
|
||||
logger.error(`Error while retrieving info on video with URL ${url} with the following message: ${err}`);
|
||||
if (err.stderr) {
|
||||
logger.error(`${err.stderr}`)
|
||||
}
|
||||
if (download) {
|
||||
download['error'] = `Failed pre-check for video info: ${err}`;
|
||||
updateDownloads();
|
||||
@@ -1561,7 +1570,7 @@ async function getUrlInfos(urls) {
|
||||
let difference = (new_date - startDate)/1000;
|
||||
logger.debug(`URL info retrieval delay: ${difference} seconds.`);
|
||||
if (err) {
|
||||
logger.error('Error during parsing:' + err);
|
||||
logger.error(`Error during parsing: ${err}`);
|
||||
resolve(null);
|
||||
}
|
||||
let try_putput = null;
|
||||
@@ -1617,13 +1626,15 @@ function checkDownloadPercent(download) {
|
||||
be divided by the "total expected bytes."
|
||||
*/
|
||||
const file_id = download['file_id'];
|
||||
const filename = path.format(path.parse(download['_filename'].substring(0, download['_filename'].length-4)));
|
||||
// assume it's a playlist for logic reasons
|
||||
const fileNames = Array.isArray(download['fileNames']) ? download['fileNames']
|
||||
: [path.format(path.parse(utils.removeFileExtension(download['_filename'])))];
|
||||
const resulting_file_size = download['filesize'];
|
||||
|
||||
if (!resulting_file_size) return;
|
||||
|
||||
glob(`${filename}*`, (err, files) => {
|
||||
let sum_size = 0;
|
||||
let sum_size = 0;
|
||||
glob(`{${fileNames.join(',')}, }*`, (err, files) => {
|
||||
files.forEach(file => {
|
||||
try {
|
||||
const file_stats = fs.statSync(file);
|
||||
@@ -1778,12 +1789,6 @@ async function checkExistsWithTimeout(filePath, timeout) {
|
||||
});
|
||||
}
|
||||
|
||||
function removeFileExtension(filename) {
|
||||
const filename_parts = filename.split('.');
|
||||
filename_parts.splice(filename_parts.length - 1)
|
||||
return filename_parts.join('.');
|
||||
}
|
||||
|
||||
app.use(function(req, res, next) {
|
||||
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
|
||||
res.header("Access-Control-Allow-Origin", getOrigin());
|
||||
@@ -1949,7 +1954,7 @@ app.get('/api/getMp3s', optionalJwt, async function(req, res) {
|
||||
// get user audio files/playlists
|
||||
auth_api.passport.authenticate('jwt')
|
||||
mp3s = auth_api.getUserVideos(req.user.uid, 'audio');
|
||||
playlists = auth_api.getUserPlaylists(req.user.uid, 'audio');
|
||||
playlists = auth_api.getUserPlaylists(req.user.uid);
|
||||
}
|
||||
|
||||
mp3s = JSON.parse(JSON.stringify(mp3s));
|
||||
@@ -1970,7 +1975,7 @@ app.get('/api/getMp4s', optionalJwt, async function(req, res) {
|
||||
// get user videos/playlists
|
||||
auth_api.passport.authenticate('jwt')
|
||||
mp4s = auth_api.getUserVideos(req.user.uid, 'video');
|
||||
playlists = auth_api.getUserPlaylists(req.user.uid, 'video');
|
||||
playlists = auth_api.getUserPlaylists(req.user.uid);
|
||||
}
|
||||
|
||||
mp4s = JSON.parse(JSON.stringify(mp4s));
|
||||
@@ -2239,7 +2244,7 @@ app.post('/api/createCategory', optionalJwt, async (req, res) => {
|
||||
name: name,
|
||||
uid: uuid(),
|
||||
rules: [],
|
||||
custom_putput: ''
|
||||
custom_output: ''
|
||||
};
|
||||
|
||||
db.get('categories').push(new_category).write();
|
||||
@@ -2610,11 +2615,11 @@ app.post('/api/deleteFile', optionalJwt, async (req, res) => {
|
||||
var wasDeleted = false;
|
||||
if (await fs.pathExists(fullpath))
|
||||
{
|
||||
wasDeleted = type === 'audio' ? await deleteAudioFile(name, path.basename(fullpath), blacklistMode) : await deleteVideoFile(name, path.basename(fullpath), blacklistMode);
|
||||
wasDeleted = type === 'audio' ? await deleteAudioFile(name, path.dirname(fullpath), blacklistMode) : await deleteVideoFile(name, path.dirname(fullpath), blacklistMode);
|
||||
db.get('files').remove({uid: uid}).write();
|
||||
wasDeleted = true;
|
||||
res.send(wasDeleted);
|
||||
} else if (video_obj) {
|
||||
} else if (file_obj) {
|
||||
db.get('files').remove({uid: uid}).write();
|
||||
wasDeleted = true;
|
||||
res.send(wasDeleted);
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
"allow_quality_select": true,
|
||||
"download_only_mode": false,
|
||||
"allow_multi_download_mode": true,
|
||||
"enable_downloads_manager": true
|
||||
"enable_downloads_manager": true,
|
||||
"allow_playlist_categorization": true
|
||||
},
|
||||
"API": {
|
||||
"use_API_key": false,
|
||||
|
||||
@@ -285,7 +285,7 @@ exports.adminExists = function() {
|
||||
|
||||
exports.getUserVideos = function(user_uid, type) {
|
||||
const user = users_db.get('users').find({uid: user_uid}).value();
|
||||
return type ? user['files'].filter(file => file.isAudio = (type === 'audio')) : user['files'];
|
||||
return type ? user['files'].filter(file => file.isAudio === (type === 'audio')) : user['files'];
|
||||
}
|
||||
|
||||
exports.getUserVideo = function(user_uid, file_uid, requireSharing = false) {
|
||||
@@ -536,14 +536,8 @@ function generateUserObject(userid, username, hash, auth_method = 'internal') {
|
||||
name: username,
|
||||
uid: userid,
|
||||
passhash: auth_method === 'internal' ? hash : null,
|
||||
files: {
|
||||
audio: [],
|
||||
video: []
|
||||
},
|
||||
playlists: {
|
||||
audio: [],
|
||||
video: []
|
||||
},
|
||||
files: [],
|
||||
playlists: [],
|
||||
subscriptions: [],
|
||||
created: Date.now(),
|
||||
role: userid === 'admin' && auth_method === 'internal' ? 'admin' : 'user',
|
||||
|
||||
@@ -33,27 +33,33 @@ Rules:
|
||||
|
||||
*/
|
||||
|
||||
async function categorize(file_json) {
|
||||
async function categorize(file_jsons) {
|
||||
// to make the logic easier, let's assume the file metadata is an array
|
||||
if (!Array.isArray(file_jsons)) file_jsons = [file_jsons];
|
||||
|
||||
let selected_category = null;
|
||||
const categories = getCategories();
|
||||
if (!categories) {
|
||||
logger.warn('Categories could not be found. Initializing categories...');
|
||||
db.assign({categories: []}).write();
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
const category = categories[i];
|
||||
const rules = category['rules'];
|
||||
|
||||
// if rules for current category apply, then that is the selected category
|
||||
if (applyCategoryRules(file_json, rules, category['name'])) {
|
||||
selected_category = category;
|
||||
logger.verbose(`Selected category ${category['name']} for ${file_json['webpage_url']}`);
|
||||
return selected_category;
|
||||
for (let i = 0; i < file_jsons.length; i++) {
|
||||
const file_json = file_jsons[i];
|
||||
for (let j = 0; j < categories.length; j++) {
|
||||
const category = categories[i];
|
||||
const rules = category['rules'];
|
||||
|
||||
// if rules for current category apply, then that is the selected category
|
||||
if (applyCategoryRules(file_json, rules, category['name'])) {
|
||||
selected_category = category;
|
||||
logger.verbose(`Selected category ${category['name']} for ${file_json['webpage_url']}`);
|
||||
return selected_category;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return selected_category;
|
||||
}
|
||||
|
||||
|
||||
@@ -197,7 +197,8 @@ DEFAULT_CONFIG = {
|
||||
"allow_quality_select": true,
|
||||
"download_only_mode": false,
|
||||
"allow_multi_download_mode": true,
|
||||
"enable_downloads_manager": true
|
||||
"enable_downloads_manager": true,
|
||||
"allow_playlist_categorization": true
|
||||
},
|
||||
"API": {
|
||||
"use_API_key": false,
|
||||
|
||||
@@ -68,6 +68,10 @@ let CONFIG_ITEMS = {
|
||||
'key': 'ytdl_enable_downloads_manager',
|
||||
'path': 'YoutubeDLMaterial.Extra.enable_downloads_manager'
|
||||
},
|
||||
'ytdl_allow_playlist_categorization': {
|
||||
'key': 'ytdl_allow_playlist_categorization',
|
||||
'path': 'YoutubeDLMaterial.Extra.allow_playlist_categorization'
|
||||
},
|
||||
|
||||
// API
|
||||
'ytdl_use_api_key': {
|
||||
|
||||
@@ -17,7 +17,7 @@ function initialize(input_db, input_users_db, input_logger) {
|
||||
|
||||
function registerFileDB(file_path, type, multiUserMode = null, sub = null, customPath = null, category = null) {
|
||||
let db_path = null;
|
||||
const file_id = file_path.substring(0, file_path.length-4);
|
||||
const file_id = utils.removeFileExtension(file_path);
|
||||
const file_object = generateFileObject(file_id, type, customPath || multiUserMode && multiUserMode.file_path, sub);
|
||||
if (!file_object) {
|
||||
logger.error(`Could not find associated JSON file for ${type} file ${file_id}`);
|
||||
|
||||
12
backend/package-lock.json
generated
12
backend/package-lock.json
generated
@@ -253,9 +253,9 @@
|
||||
"integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.21.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz",
|
||||
"integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==",
|
||||
"version": "0.21.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
||||
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.10.0"
|
||||
}
|
||||
@@ -1081,9 +1081,9 @@
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
|
||||
"integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA=="
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz",
|
||||
"integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg=="
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1",
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"dependencies": {
|
||||
"archiver": "^3.1.1",
|
||||
"async": "^3.1.0",
|
||||
"axios": "^0.21.0",
|
||||
"axios": "^0.21.1",
|
||||
"bcryptjs": "^2.4.0",
|
||||
"compression": "^1.7.4",
|
||||
"config": "^3.2.3",
|
||||
|
||||
@@ -211,7 +211,7 @@ async function deleteSubscriptionFile(sub, file, deleteForever, file_uid = null,
|
||||
var jsonPath = path.join(__dirname,filePath,name+'.info.json');
|
||||
var videoFilePath = path.join(__dirname,filePath,name+ext);
|
||||
var imageFilePath = path.join(__dirname,filePath,name+'.jpg');
|
||||
var altImageFilePath = path.join(__dirname,filePath,name+'.jpg');
|
||||
var altImageFilePath = path.join(__dirname,filePath,name+'.webp');
|
||||
|
||||
const [jsonExists, videoFileExists, imageFileExists, altImageFileExists] = await Promise.all([
|
||||
fs.pathExists(jsonPath),
|
||||
|
||||
@@ -105,20 +105,26 @@ function getDownloadedThumbnail(name, type, customPath = null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function getExpectedFileSize(info_json) {
|
||||
if (info_json['filesize']) {
|
||||
return info_json['filesize'];
|
||||
}
|
||||
function getExpectedFileSize(input_info_jsons) {
|
||||
// treat single videos as arrays to have the file sizes checked/added to. makes the code cleaner
|
||||
const info_jsons = Array.isArray(input_info_jsons) ? input_info_jsons : [input_info_jsons];
|
||||
|
||||
const formats = info_json['format_id'].split('+');
|
||||
let expected_filesize = 0;
|
||||
formats.forEach(format_id => {
|
||||
if (!info_json.formats) return expected_filesize;
|
||||
info_json.formats.forEach(available_format => {
|
||||
if (available_format.format_id === format_id && available_format.filesize) {
|
||||
expected_filesize += available_format.filesize;
|
||||
}
|
||||
info_jsons.forEach(info_json => {
|
||||
if (info_json['filesize']) {
|
||||
expected_filesize += info_json['filesize'];
|
||||
return;
|
||||
}
|
||||
const formats = info_json['format_id'].split('+');
|
||||
let individual_expected_filesize = 0;
|
||||
formats.forEach(format_id => {
|
||||
info_json.formats.forEach(available_format => {
|
||||
if (available_format.format_id === format_id && available_format.filesize) {
|
||||
individual_expected_filesize += available_format.filesize;
|
||||
}
|
||||
});
|
||||
});
|
||||
expected_filesize += individual_expected_filesize;
|
||||
});
|
||||
|
||||
return expected_filesize;
|
||||
@@ -181,6 +187,12 @@ async function recFindByExt(base,ext,files,result)
|
||||
return result
|
||||
}
|
||||
|
||||
function removeFileExtension(filename) {
|
||||
const filename_parts = filename.split('.');
|
||||
filename_parts.splice(filename_parts.length - 1);
|
||||
return filename_parts.join('.');
|
||||
}
|
||||
|
||||
// objects
|
||||
|
||||
function File(id, title, thumbnailURL, isAudio, duration, url, uploader, size, path, upload_date, description, view_count, height, abr) {
|
||||
@@ -210,5 +222,6 @@ module.exports = {
|
||||
deleteJSONFile: deleteJSONFile,
|
||||
getDownloadedFilesByType: getDownloadedFilesByType,
|
||||
recFindByExt: recFindByExt,
|
||||
removeFileExtension: removeFileExtension,
|
||||
File: File
|
||||
}
|
||||
|
||||
@@ -127,12 +127,11 @@ export class RecentVideosComponent implements OnInit {
|
||||
this.normal_files_received = false;
|
||||
this.postsService.getAllFiles().subscribe(res => {
|
||||
this.files = res['files'];
|
||||
this.files.sort(this.sortFiles);
|
||||
for (let i = 0; i < this.files.length; i++) {
|
||||
const file = this.files[i];
|
||||
file.duration = typeof file.duration !== 'string' ? file.duration : this.durationStringToNumber(file.duration);
|
||||
file.index = i;
|
||||
}
|
||||
this.files.sort(this.sortFiles);
|
||||
if (this.search_mode) {
|
||||
this.filterFiles(this.search_text);
|
||||
} else {
|
||||
@@ -239,19 +238,17 @@ export class RecentVideosComponent implements OnInit {
|
||||
const blacklistMode = args.blacklistMode;
|
||||
|
||||
if (file.sub_id) {
|
||||
this.deleteSubscriptionFile(file, index, blacklistMode);
|
||||
this.deleteSubscriptionFile(file, blacklistMode);
|
||||
} else {
|
||||
this.deleteNormalFile(file, index, blacklistMode);
|
||||
this.deleteNormalFile(file, blacklistMode);
|
||||
}
|
||||
}
|
||||
|
||||
deleteNormalFile(file, index, blacklistMode = false) {
|
||||
deleteNormalFile(file, blacklistMode = false) {
|
||||
this.postsService.deleteFile(file.uid, file.isAudio ? 'audio' : 'video', blacklistMode).subscribe(result => {
|
||||
if (result) {
|
||||
this.postsService.openSnackBar('Delete success!', 'OK.');
|
||||
this.files.splice(file.index, 1);
|
||||
for (let i = 0; i < this.files.length; i++) { this.files[i].index = i }
|
||||
this.filterByProperty(this.filterProperty['property']);
|
||||
this.removeFileCard(file);
|
||||
} else {
|
||||
this.postsService.openSnackBar('Delete failed!', 'OK.');
|
||||
}
|
||||
@@ -260,30 +257,39 @@ export class RecentVideosComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
deleteSubscriptionFile(file, index, blacklistMode = false) {
|
||||
deleteSubscriptionFile(file, blacklistMode = false) {
|
||||
if (blacklistMode) {
|
||||
this.deleteForever(file, index);
|
||||
this.deleteForever(file);
|
||||
} else {
|
||||
this.deleteAndRedownload(file, index);
|
||||
this.deleteAndRedownload(file);
|
||||
}
|
||||
}
|
||||
|
||||
deleteAndRedownload(file, index) {
|
||||
deleteAndRedownload(file) {
|
||||
const sub = this.postsService.getSubscriptionByID(file.sub_id);
|
||||
this.postsService.deleteSubscriptionFile(sub, file.id, false, file.uid).subscribe(res => {
|
||||
this.postsService.openSnackBar(`Successfully deleted file: '${file.id}'`);
|
||||
this.files.splice(index, 1);
|
||||
this.removeFileCard(file);
|
||||
});
|
||||
}
|
||||
|
||||
deleteForever(file, index) {
|
||||
deleteForever(file) {
|
||||
const sub = this.postsService.getSubscriptionByID(file.sub_id);
|
||||
this.postsService.deleteSubscriptionFile(sub, file.id, true, file.uid).subscribe(res => {
|
||||
this.postsService.openSnackBar(`Successfully deleted file: '${file.id}'`);
|
||||
this.files.splice(index, 1);
|
||||
this.removeFileCard(file);
|
||||
});
|
||||
}
|
||||
|
||||
removeFileCard(file_to_remove) {
|
||||
const index = this.files.map(e => e.uid).indexOf(file_to_remove.uid);
|
||||
this.files.splice(index, 1);
|
||||
if (this.search_mode) {
|
||||
this.filterFiles(this.search_text);
|
||||
}
|
||||
this.filterByProperty(this.filterProperty['property']);
|
||||
}
|
||||
|
||||
// sorting and filtering
|
||||
|
||||
sortFiles(a, b) {
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: 5px;
|
||||
z-index: 99999;
|
||||
z-index: 999;
|
||||
width: calc(100% - 8px);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -365,6 +365,7 @@ export class PostsService implements CanActivate {
|
||||
}
|
||||
|
||||
deleteSubscriptionFile(sub, file, deleteForever, file_uid) {
|
||||
delete sub['videos'];
|
||||
return this.http.post(this.path + 'deleteSubscriptionFile', {sub: sub, file: file, deleteForever: deleteForever,
|
||||
file_uid: file_uid}, this.httpOptions)
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3 mb-2">
|
||||
<div class="col-12 mt-3">
|
||||
<h6 i18n="Categories">Categories</h6>
|
||||
<div cdkDropList class="category-list" (cdkDropListDropped)="dropCategory($event)">
|
||||
<div class="category-box" *ngFor="let category of postsService.categories" cdkDrag>
|
||||
@@ -154,6 +154,9 @@
|
||||
</div>
|
||||
<button style="margin-top: 10px;" mat-mini-fab (click)="openAddCategoryDialog()"><mat-icon>add</mat-icon></button>
|
||||
</div>
|
||||
<div class="col-12 mt-2 mb-2">
|
||||
<mat-checkbox [(ngModel)]="new_config['Extra']['allow_playlist_categorization']" matTooltip="With this setting enabled, if a single video matches a category, the entire playlist will receive that category." i18n-matTooltip="Allow playlist categorization setting tooltip"><ng-container i18n="Allow playlist categorization setting label">Allow playlist categorization</ng-container></mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
@@ -20,7 +20,7 @@ import { EditCategoryDialogComponent } from 'app/dialogs/edit-category-dialog/ed
|
||||
})
|
||||
export class SettingsComponent implements OnInit {
|
||||
all_locales = isoLangs;
|
||||
supported_locales = ['en', 'es', 'de', 'fr', 'zh', 'nb', 'en-GB'];
|
||||
supported_locales = ['en', 'es', 'de', 'fr', 'zh', 'nb', 'it', 'en-GB'];
|
||||
initialLocale = localStorage.getItem('locale');
|
||||
|
||||
initial_config = null;
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
"download_only_mode": false,
|
||||
"allow_multi_download_mode": true,
|
||||
"settings_pin_required": false,
|
||||
"enable_downloads_manager": true
|
||||
"enable_downloads_manager": true,
|
||||
"allow_playlist_categorization": true
|
||||
},
|
||||
"API": {
|
||||
"use_API_key": false,
|
||||
|
||||
@@ -241,5 +241,31 @@
|
||||
"3697f8583ea42868aa269489ad366103d94aece7": "Editando",
|
||||
"0c43af932e6a4ee85500e28f01b3538b4eb27bc4": "Nivel de registro",
|
||||
"a8b7b9c168fd936a75e500806a8c0d7755ef1198": "NOTA: La carga de cookies nuevas anulará las cookies anteriores y las cookies son para toda la instancia, no por usuario.",
|
||||
"511b600ae4cf037e4eb3b7a58410842cd5727490": "Agregar más contenido"
|
||||
"511b600ae4cf037e4eb3b7a58410842cd5727490": "Agregar más contenido",
|
||||
"56a2a773fbd5a6b9ac2e6b89d29d70a2ed0f3227": "Ver menos.",
|
||||
"ddc31f2885b1b33a7651963254b0c197f2a64086": "Ver más.",
|
||||
"c776eb4992b6c98f58cd89b20c1ea8ac37888521": "Seleccione un agente de descarga",
|
||||
"5fb1e0083c9b2a40ac8ae7dcb2618311c291b8b9": "Descarga automática de Twitch Chat",
|
||||
"84ffcebac2709ca0785f4a1d5ba274433b5beabc": "También conocido como ID de cliente.",
|
||||
"8ae23bc4302a479f687f4b20a84c276182e2519c": "Clave de API de Twitch",
|
||||
"d162f9fcd6a7187b391e004f072ab3da8377c47d": "Usar la API de Twitch",
|
||||
"04201f9d27abd7d6f58a4328ab98063ce1072006": "Categorías",
|
||||
"ef418d4ece7c844f3a5e431da1aa59bedd88da7b": "Args personalizados globales",
|
||||
"1148fd45287ff09955b938756bc302042bcb29c7": "La ruta es relativa a las rutas de descarga anteriores. No incluya la extensión.",
|
||||
"cfe829634b1144bc44b6d38cf5584ea65db9804f": "Salida de archivo predeterminada",
|
||||
"3d1a47dc18b7bd8b5d9e1eb44b235ed9c4a2b513": "Volver a descargar nuevas cargas",
|
||||
"13759b09a7f4074ceee8fa2f968f9815fdf63295": "A veces, los videos nuevos se descargan antes de procesarse por completo. Esta configuración significará que los nuevos videos se verificarán para una versión de mayor calidad al día siguiente.",
|
||||
"dad95154dcef3509b8cc705046061fd24994bbb7": "vistas",
|
||||
"792dc6a57f28a1066db283f2e736484f066005fd": "Descargar Twitch Chat",
|
||||
"e4eeb9106dbcbc91ca1ac3fb4068915998a70f37": "Añadir nueva regla",
|
||||
"2489eefea00931942b91f4a1ae109514b591e2e1": "Reglas",
|
||||
"c3b0b86523f1d10e84a71f9b188d54913a11af3b": "Editando la categoría",
|
||||
"07db550ae114d9faad3a0cbb68bcc16ab6cd31fc": "Pausado",
|
||||
"73423607944a694ce6f9e55cfee329681bb4d9f9": "No se encontraron vídeos.",
|
||||
"29376982b1205d9d6ea3d289e8e2f8e1ac2839b1": "Orden inverso",
|
||||
"33026f57ea65cd9c8a5d917a08083f71a718933a": "Orden normal",
|
||||
"5caadefa4143cf6766a621b0f54f91f373a1f164": "Añadir contenido",
|
||||
"0cc1dec590ecd74bef71a865fb364779bc42a749": "Categoría:",
|
||||
"303e45ffae995c9817e510e38cb969e6bb3adcbf": "(Pausado)",
|
||||
"d641b8fa5ac5e85114c733b1f7de6976bd091f70": "Calidad máxima"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
249
src/assets/i18n/messages.it.json
Normal file
249
src/assets/i18n/messages.it.json
Normal file
@@ -0,0 +1,249 @@
|
||||
{
|
||||
"17f0ea5d2d7a262b0e875acc70475f102aee84e6": "Crea una scaletta",
|
||||
"cff1428d10d59d14e45edec3c735a27b5482db59": "Nome",
|
||||
"f0baeb8b69d120073b6d60d34785889b0c3232c8": "Audio",
|
||||
"2d1ea268a6a9f483dbc2cbfe19bf4256a57a6af4": "Video",
|
||||
"f61c6867295f3b53d23557021f2f4e0aa1d0b8fc": "Tipo",
|
||||
"f47e2d56dd8a145b2e9599da9730c049d52962a2": "File audio",
|
||||
"a52dae09be10ca3a65da918533ced3d3f4992238": "Video",
|
||||
"d9e83ac17026e70ef6e9c0f3240a3b2450367f40": "Modifica i parametri di youtube-dl",
|
||||
"7fc1946abe2b40f60059c6cd19975d677095fd19": "Simula i nuovi parametri",
|
||||
"0b71824ae71972f236039bed43f8d2323e8fd570": "Aggiungi un'impostazione",
|
||||
"c8b0e59eb491f2ac7505f0fbab747062e6b32b23": "Cerca per categoria",
|
||||
"9eeb91caef5a50256dd87e1c4b7b3e8216479377": "Usa valore impostato",
|
||||
"25d8ad5eba2ec24e68295a27d6a4bb9b49e3dacd": "Valore impostato",
|
||||
"7de2451ed3fb8d8b847979bd3f0c740b970f167b": "Aggiungi impostazione",
|
||||
"d7b35c384aecd25a516200d6921836374613dfe7": "Annulla",
|
||||
"b2623aee44b70c9a4ba1fce16c8a593b0a4c7974": "Modifica",
|
||||
"a38ae1082fec79ba1f379978337385a539a28e73": "Qualità",
|
||||
"4be966a9dcfbc9b54dfcc604b831c0289f847fa4": "Utilizza URL",
|
||||
"d3f02f845e62cebd75fde451ab8479d2a8ad784d": "Visualizza",
|
||||
"4a9889d36910edc8323d7bab60858ab3da6d91df": "Solo audio",
|
||||
"96a01fafe135afc58b0f8071a4ab00234495ce18": "Modalità download multiplo",
|
||||
"6a21ba5fb0ac804a525bf9ab168038c3ee88e661": "Scarica",
|
||||
"6a3777f913cf3f288664f0632b9f24794fdcc24e": "Annulla",
|
||||
"322ed150e02666fe2259c5b4614eac7066f4ffa0": "Avanzato",
|
||||
"b7ffe7c6586d6f3f18a9246806a7c7d5538ab43e": "Comando simulato:",
|
||||
"4e4c721129466be9c3862294dc40241b64045998": "Usa parametri personalizzati",
|
||||
"ad2f8ac8b7de7945b80c8e424484da94e597125f": "Parametri personalizzati",
|
||||
"a6911c2157f1b775284bbe9654ce5eb30cf45d7f": "Non è necessario includere l'URL, solo ciò che viene dopo. I parametri sono delimitati da due vigole: ,,",
|
||||
"3a92a3443c65a52f37ca7efb8f453b35dbefbf29": "Usa output personalizzato",
|
||||
"d9c02face477f2f9cdaae318ccee5f89856851fb": "Output personalizzata",
|
||||
"fcfd4675b4c90f08d18d3abede9a9a4dff4cfdc7": "Documentazione",
|
||||
"19d1ae64d94d28a29b2c57ae8671aace906b5401": "Il percorso è relativo al percorso di download configurato. Non includere l'estensione.",
|
||||
"8fad10737d3e3735a6699a4d89cbf6c20f6bb55f": "Utilizza l'autenticazione",
|
||||
"08c74dc9762957593b91f6eb5d65efdfc975bf48": "Nome utente",
|
||||
"c32ef07f8803a223a83ed17024b38e8d82292407": "Password",
|
||||
"616e206cb4f25bd5885fc35925365e43cf5fb929": "Nome:",
|
||||
"c52db455cca9109ee47e1a612c3f4117c09eb71b": "URL:",
|
||||
"c6eb45d085384903e53ab001a3513d1de6a1dbac": "Caricato da:",
|
||||
"109c6f4a5e46efb933612ededfaf52a13178b7e0": "Dimensioni file:",
|
||||
"bd630d8669b16e5f264ec4649d9b469fe03e5ff4": "Percorso:",
|
||||
"a67e7d843cef735c79d5ef1c8ba4af3e758912bb": "Data di caricamento:",
|
||||
"f4e529ae5ffd73001d1ff4bbdeeb0a72e342e5c8": "Chiudi",
|
||||
"4f389e41e4592f7f9bb76abdd8af4afdfb13f4f1": "Modifica playlist",
|
||||
"511b600ae4cf037e4eb3b7a58410842cd5727490": "Aggiungi più contenuto",
|
||||
"52c9a103b812f258bcddc3d90a6e3f46871d25fe": "Salva",
|
||||
"ca3dbbc7f3e011bffe32a10a3ea45cc84f30ecf1": "ID:",
|
||||
"e684046d73bcee88e82f7ff01e2852789a05fc32": "Conteggio:",
|
||||
"28f86ffd419b869711aa13f5e5ff54be6d70731c": "Modifica",
|
||||
"826b25211922a1b46436589233cb6f1a163d89b7": "Elimina",
|
||||
"321e4419a943044e674beb55b8039f42a9761ca5": "Informazioni",
|
||||
"34504b488c24c27e68089be549f0eeae6ebaf30b": "Elimina e aggiungi alla lista nera",
|
||||
"ebadf946ae90f13ecd0c70f09edbc0f983af8a0f": "Carica nuovi cookie",
|
||||
"98a8a42e5efffe17ab786636ed0139b4c7032d0e": "Trascina e rilascia",
|
||||
"a8b7b9c168fd936a75e500806a8c0d7755ef1198": "NOTA: il caricamento di nuovi cookie sovrascriverà i cookie precedenti. Inoltre tieni presente che i cookie sono a livello di processo, non per utente.",
|
||||
"121cc5391cd2a5115bc2b3160379ee5b36cd7716": "Impostazioni",
|
||||
"801b98c6f02fe3b32f6afa3ee854c99ed83474e6": "URL",
|
||||
"54c512cca1923ab72faf1a0bd98d3d172469629a": "URL con cui si accederà a questa applicazione, senza porta.",
|
||||
"cb2741a46e3560f6bc6dfd99d385e86b08b26d72": "Porta",
|
||||
"22e8f1d0423a3b784fe40fab187b92c06541b577": "Porta personalizzata. La predefinita è 17442.",
|
||||
"d4477669a560750d2064051a510ef4d7679e2f3e": "Modalità multiutente",
|
||||
"2eb03565fcdce7a7a67abc277a936a32fcf51557": "Percorso profili utente",
|
||||
"a64505c41150663968e277ec9b3ddaa5f4838798": "Percorso per profili utente e per video scaricati da ognuno.",
|
||||
"4e3120311801c4acd18de7146add2ee4a4417773": "Consenti iscrizioni",
|
||||
"4bee2a4bef2d26d37c9b353c278e24e5cd309ce3": "Percorso salvataggio playlist sottoscritte",
|
||||
"bc9892814ee2d119ae94378c905ea440a249b84a": "Percorso salvataggio per i video dei canali e delle playlist sottoscritte. È relativo alla cartella principale di YTDL-Material.",
|
||||
"5bef4b25ba680da7fff06b86a91b1fc7e6a926e3": "Intervallo di verifica",
|
||||
"0f56a7449b77630c114615395bbda4cab398efd8": "Unità in secondi, inserire solo numeri.",
|
||||
"27a56aad79d8b61269ed303f11664cc78bcc2522": "Tema",
|
||||
"ff7cee38a2259526c519f878e71b964f41db4348": "Predefinito",
|
||||
"adb4562d2dbd3584370e44496969d58c511ecb63": "Scuro",
|
||||
"7a6bacee4c31cb5c0ac2d24274fb4610d8858602": "Consenti variazione tema",
|
||||
"fe46ccaae902ce974e2441abe752399288298619": "Lingua",
|
||||
"82421c3e46a0453a70c42900eab51d58d79e6599": "Principale",
|
||||
"ab2756805742e84ad0cc0468f4be2d8aa9f855a5": "Percorso della cartella audio",
|
||||
"c2c89cdf45d46ea64d2ed2f9ac15dfa4d77e26ca": "Percorso per download solo audio. È relativo alla cartella principale di YTDL-Material.",
|
||||
"46826331da1949bd6fb74624447057099c9d20cd": "Percorso cartella Video",
|
||||
"17c92e6d47a213fa95b5aa344b3f258147123f93": "Percorso per il download di video. È relativo alla cartella principale di YTDL-Material.",
|
||||
"6b995e7130b4d667eaab6c5f61b362ace486d26d": "Parametri personalizzati generali per i download sulla home page. I parametri sono delimitati da due virgole: ,,",
|
||||
"78e49b7339b4fa7184dd21bcaae107ce9b7076f6": "Usa l'archivio youtube-dl",
|
||||
"ffc19f32b1cba0daefc0e5668f89346db1db83ad": "Includi anteprima",
|
||||
"384de8f8f112c9e6092eb2698706d391553f3e8d": "Includi metadati",
|
||||
"fb35145bfb84521e21b6385363d59221f436a573": "Interrompi tutti i download",
|
||||
"0ba25ad86a240576c4f20a2fada4722ebba77b1e": "Scaricato da",
|
||||
"61f8fd90b5f8cb20c70371feb2ee5e1fac5a9095": "Titolo della barra superiore",
|
||||
"78d3531417c0d4ba4c90f0d4ae741edc261ec8df": "Abilita il file manager",
|
||||
"a5a1be0a5df07de9eec57f5d2a86ed0204b2e75a": "Abilita il download manager",
|
||||
"c33bd5392b39dbed36b8e5a1145163a15d45835f": "Consenti la selezione della qualità",
|
||||
"bda5508e24e0d77debb28bcd9194d8fefb1cfb92": "Modalità solo download",
|
||||
"09d31c803a7252658694e1e3176b97f5655a3fe3": "Consenti la modalità di download multiplo",
|
||||
"1c4dbce56d96b8974aac24a02f7ab2ee81415014": "Abilita l'API Pubblica",
|
||||
"23bd81dcc30b74d06279a26d7a42e8901c1b124e": "Chiave API Pubblica",
|
||||
"41016a73d8ad85e6cb26dffa0a8fab9fe8f60d8e": "Visualizza la documentazione",
|
||||
"1b258b258b4cc475ceb2871305b61756b0134f4a": "Genera",
|
||||
"00a94f58d9eb2e3aa561440eabea616d0c937fa2": "Cancellerai la tua chiave API precedente!",
|
||||
"d5d7c61349f3b0859336066e6d453fc35d334fe5": "Usa l'API di YouTube",
|
||||
"ce10d31febb3d9d60c160750570310f303a22c22": "Chiave API YouTube",
|
||||
"8602e313cdfa7c4cc475ccbe86459fce3c3fd986": "Generare una chiave è facile!",
|
||||
"9b3cedfa83c6d7acb3210953289d1be4aab115c7": "Premi qui",
|
||||
"7f09776373995003161235c0c8d02b7f91dbc4df": "per scaricare manualmente l'estensione ufficiale per Chrome di YoutubeDL-Material.",
|
||||
"5b5296423906ab3371fdb2b5a5aaa83acaa2ee52": "È necessario installare manualmente l'estensione e modificarne le impostazioni inserendo l'URL della pagina principale.",
|
||||
"9a2ec6da48771128384887525bdcac992632c863": "per installare l'estensione ufficiale YoutubeDL-Material per Firefox direttamente dalla pagina delle estensioni di Firefox.",
|
||||
"eb81be6b49e195e5307811d1d08a19259d411f37": "Istruzioni dettagliate per la configurazione.",
|
||||
"cb17ff8fe3961cf90f44bee97c88a3f3347a7e55": "Viene richiesto semplicemente di modificare le impostazioni dell'estensione, inserendo l'URL della pagina principale.",
|
||||
"61b81b11aad0b9d970ece2fce18405f07eac69c2": "Trascina il link qui sotto tra tuoi preferiti e sei a posto! Vai al video YouTube che desideri scaricare e fai clic sul preferito.",
|
||||
"c505d6c5de63cc700f0aaf8a4b31fae9e18024e5": "Genera un preferito \"solo audio\"",
|
||||
"d5f69691f9f05711633128b5a3db696783266b58": "Extra",
|
||||
"5fab47f146b0a4b809dcebf3db9da94df6299ea1": "Usa agente di download predefinito",
|
||||
"ec71e08aee647ea4a71fd6b7510c54d84a797ca6": "Seleziona un metodo di download",
|
||||
"0c43af932e6a4ee85500e28f01b3538b4eb27bc4": "Livello di Log",
|
||||
"db6c192032f4cab809aad35215f0aa4765761897": "Scadenza accesso",
|
||||
"dc3d990391c944d1fbfc7cfb402f7b5e112fb3a8": "Consenti download avanzato",
|
||||
"431e5f3a0dde88768d1074baedd65266412b3f02": "Usa i cookie",
|
||||
"80651a7ad1229ea6613557d3559f702cfa5aecf5": "Imposta i cookie",
|
||||
"bc2e854e111ecf2bd7db170da5e3c2ed08181d88": "Avanzate",
|
||||
"37224420db54d4bc7696f157b779a7225f03ca9d": "Consenti registrazione utente",
|
||||
"4f56ced9d6b85aeb1d4346433361d47ea72dac1a": "Interno",
|
||||
"e3d7c5f019e79a3235a28ba24df24f11712c7627": "LDAP",
|
||||
"fa548cee6ea11c160a416cac3e6bdec0363883dc": "Metodo di autenticazione",
|
||||
"1db9789b93069861019bd0ccaa5d4706b00afc61": "URL LDAP",
|
||||
"f50fa6c09c8944aed504f6325f2913ee6c7a296a": "Associa DN",
|
||||
"080cc6abcba236390fc22e79792d0d3443a3bd2a": "Associa credenziali",
|
||||
"cfa67d14d84fe0e9fadf251dc51ffc181173b662": "Base di ricerca",
|
||||
"e01d54ecc1a0fcf9525a3c100ed8b83d94e61c23": "Filtro di ricerca",
|
||||
"4d13a9cd5ed3dcee0eab22cb25198d43886942be": "Utenti",
|
||||
"eb3d5aefff38a814b76da74371cbf02c0789a1ef": "Registri",
|
||||
"fe8fd36dbf5deee1d56564965787a782a66eba44": "{VAR_SELECT, seleziona, vero {Close} falso {Cancel} altro {otha}}",
|
||||
"cec82c0a545f37420d55a9b6c45c20546e82f94e": "Informazioni su YoutubeDL-Material",
|
||||
"199c17e5d6a419313af3c325f06dcbb9645ca618": "è un downloader di YouTube open source costruito secondo le specifiche Material Design di Google. Puoi scaricare agevolmente i tuoi video preferiti come file video o solo audio e persino iscriverti ai tuoi canali e playlist preferiti per tenerti aggiornato con i nuovi video pubblicati.",
|
||||
"bc0ad0ee6630acb7fcb7802ec79f5a0ee943c1a7": "ha alcune fantastiche funzionalità incluse! Una solida API, supporto Docker e supporto per la localizzazione (traduzione). Leggi tutte le funzionalità supportate cliccando sull'icona GitHub in alto.",
|
||||
"a45e3b05f0529dc5246d70ef62304c94426d4c81": "Versione installata:",
|
||||
"e22f3a5351944f3a1a10cfc7da6f65dfbe0037fe": "Verifica aggiornamenti in corso...",
|
||||
"a16e92385b4fd9677bb830a4b796b8b79c113290": "Aggiornamento disponibile",
|
||||
"189b28aaa19b3c51c6111ad039c4fd5e2a22e370": "È possibile eseguire l'aggiornamento dal menù impostazioni.",
|
||||
"b33536f59b94ec935a16bd6869d836895dc5300c": "Hai trovato un errore o hai un suggerimento?",
|
||||
"e1f398f38ff1534303d4bb80bd6cece245f24016": "per segnalare un problema!",
|
||||
"42ff677ec14f111e88bd6cdd30145378e994d1bf": "Il tuo profilo",
|
||||
"ac9d09de42edca1296371e4d801349c9096ac8de": "UID:",
|
||||
"a5ed099ffc9e96f6970df843289ade8a7d20ab9f": "Creato:",
|
||||
"fa96f2137af0a24e6d6d54c598c0af7d5d5ad344": "Non hai eseguito l'accesso.",
|
||||
"6765b4c916060f6bc42d9bb69e80377dbcb5e4e9": "Accedi",
|
||||
"bb694b49d408265c91c62799c2b3a7e3151c824d": "Esci",
|
||||
"a1dbca87b9f36d2b06a5cbcffb5814c4ae9b798a": "Crea un account amministratore",
|
||||
"2d2adf3ca26a676bca2269295b7455a26fd26980": "Nessun account amministratore predefinito rilevato. Verrà creato e impostata la password per un account amministratore con il nome utente \"admin\".",
|
||||
"70a67e04629f6d412db0a12d51820b480788d795": "Crea",
|
||||
"994363f08f9fbfa3b3994ff7b35c6904fdff18d8": "Profilo",
|
||||
"004b222ff9ef9dd4771b777950ca1d0e4cd4348a": "Informazioni",
|
||||
"92eee6be6de0b11c924e3ab27db30257159c0a7c": "Pagina principale",
|
||||
"357064ca9d9ac859eb618e28e8126fa32be049e2": "Iscrizioni",
|
||||
"822fab38216f64e8166d368b59fe756ca39d301b": "Download",
|
||||
"a249a5ae13e0835383885aaf697d2890cc3e53e9": "Condividi playlist",
|
||||
"15da89490e04496ca9ea1e1b3d44fb5efd4a75d9": "Condividi il video",
|
||||
"1d540dcd271b316545d070f9d182c372d923aadd": "Condividi l'audio",
|
||||
"1f6d14a780a37a97899dc611881e6bc971268285": "Abilita la condivisione",
|
||||
"6580b6a950d952df847cb3d8e7176720a740adc8": "Usa data e ora",
|
||||
"4f2ed9e71a7c981db3e50ae2fedb28aff2ec4e6c": "Secondi",
|
||||
"3a6e5a6aa78ca864f6542410c5dafb6334538106": "Copia negli appunti",
|
||||
"5b3075e8dc3f3921ec316b0bd83b6d14a06c1a4f": "Salva le modifiche",
|
||||
"4d8a18b04a1f785ecd8021ac824e0dfd5881dbfc": "Il download è riuscito",
|
||||
"348cc5d553b18e862eb1c1770e5636f6b05ba130": "Si è verificato un errore",
|
||||
"4f8b2bb476981727ab34ed40fde1218361f92c45": "Dettagli",
|
||||
"e9aff8e6df2e2bf6299ea27bb2894c70bc48bd4d": "Si è verificato un errore:",
|
||||
"77b0c73840665945b25bd128709aa64c8f017e1c": "Inizio download:",
|
||||
"08ff9375ec078065bcdd7637b7ea65fce2979266": "Termine download:",
|
||||
"ad127117f9471612f47d01eae09709da444a36a4": "Percorso(i) file:",
|
||||
"a9806cf78ce00eb2613eeca11354a97e033377b8": "Iscriviti alla playlist o al canale",
|
||||
"93efc99ae087fc116de708ecd3ace86ca237cf30": "URL della playlist o del canale",
|
||||
"08f5d0ef937ae17feb1b04aff15ad88911e87baf": "Nome personalizzato",
|
||||
"ea30873bd3f0d5e4fb2378eec3f0a1db77634a28": "Scarica tutti i file caricati",
|
||||
"28a678e9cabf86e44c32594c43fa0e890135c20f": "Scarica i video caricati negli ultimi",
|
||||
"c76a955642714b8949ff3e4b4990864a2e2cac95": "Modalità solo audio",
|
||||
"408ca4911457e84a348cecf214f02c69289aa8f1": "Modalità solo streaming",
|
||||
"f432e1a8d6adb12e612127978ce2e0ced933959c": "Questi vengono aggiunti dopo ai parametri standard.",
|
||||
"98b6ec9ec138186d663e64770267b67334353d63": "File di uscita personalizzato",
|
||||
"d0336848b0c375a1c25ba369b3481ee383217a4f": "Iscriviti",
|
||||
"e78c0d60ac39787f62c9159646fe0b3c1ed55a1d": "Tipo:",
|
||||
"a44d86aa1e6c20ced07aca3a7c081d8db9ded1c6": "Archivio:",
|
||||
"8efc77bf327659c0fec1f518cf48a98cdcd9dddf": "Esporta archivio",
|
||||
"3042bd3ad8dffcfeca5fd1ae6159fd1047434e95": "Annulla l'iscrizione",
|
||||
"e2319dec5b4ccfb6ed9f55ccabd63650a8fdf547": "I tuoi abbonamenti",
|
||||
"807cf11e6ac1cde912496f764c176bdfdd6b7e19": "Canali",
|
||||
"29b89f751593e1b347eef103891b7a1ff36ec03f": "Nome non disponibile. Recupero del canale in corso.",
|
||||
"4636cd4a1379c50d471e98786098c4d39e1e82ad": "Non sei iscritto a nessun canale.",
|
||||
"47546e45bbb476baaaad38244db444c427ddc502": "Scalette",
|
||||
"2e0a410652cb07d069f576b61eab32586a18320d": "Nome non disponibile. Recupero playlist in corso.",
|
||||
"587b57ced54965d8874c3fd0e9dfedb987e5df04": "Non sei iscritto a nessuna playlist.",
|
||||
"3697f8583ea42868aa269489ad366103d94aece7": "Modifica",
|
||||
"7e892ba15f2c6c17e83510e273b3e10fc32ea016": "Cerca",
|
||||
"2054791b822475aeaea95c0119113de3200f5e1c": "Durata:",
|
||||
"94e01842dcee90531caa52e4147f70679bac87fe": "Elimina e scarica di nuovo",
|
||||
"2031adb51e07a41844e8ba7704b054e98345c9c1": "Elimina definitivamente",
|
||||
"91ecce65f1d23f9419d1c953cd6b7bc7f91c110e": "Aggiornato da",
|
||||
"1372e61c5bd06100844bd43b98b016aabc468f62": "Seleziona una versione:",
|
||||
"cfc2f436ec2beffb042e7511a73c89c372e86a6c": "Registrati",
|
||||
"a1ad8b1be9be43b5183bd2c3186d4e19496f2a0b": "ID sessione:",
|
||||
"eb98135e35af26a9a326ee69bd8ff104d36dd8ec": "(attuale)",
|
||||
"b6c453e0e61faea184bbaf5c5b0a1e164f4de2a2": "Cancella tutti i download",
|
||||
"7117fc42f860e86d983bfccfcf2654e5750f3406": "Nessun download disponibile!",
|
||||
"b7ff2e2b909c53abe088fe60b9f4b6ac7757247f": "Registra un utente",
|
||||
"024886ca34a6f309e3e51c2ed849320592c3faaa": "Nome utente",
|
||||
"2bd201aea09e43fbfd3cd15ec0499b6755302329": "Gestisci utente",
|
||||
"29c97c8e76763bb15b6d515648fa5bd1eb0f7510": "UID utente:",
|
||||
"e70e209561583f360b1e9cefd2cbb1fe434b6229": "Nuova password",
|
||||
"6498fa1b8f563988f769654a75411bb8060134b9": "Imposta nuova password",
|
||||
"544e09cdc99a8978f48521d45f62db0da6dcf742": "Usa il ruolo predefinito",
|
||||
"4f20f2d5a6882190892e58b85f6ccbedfa737952": "Sì",
|
||||
"3d3ae7deebc5949b0c1c78b9847886a94321d9fd": "No",
|
||||
"57c6c05d8ebf4ef1180c2705033c044f655bb2c4": "Gestisci ruolo",
|
||||
"746f64ddd9001ac456327cd9a3d5152203a4b93c": "Nome utente",
|
||||
"52c1447c1ec9570a2a3025c7e566557b8d19ed92": "Ruolo",
|
||||
"59a8c38db3091a63ac1cb9590188dc3a972acfb3": "Azioni",
|
||||
"632e8b20c98e8eec4059a605a4b011bb476137af": "Modifica utente",
|
||||
"95b95a9c79e4fd9ed41f6855e37b3b06af25bcab": "Elimina utente",
|
||||
"4d92a0395dd66778a931460118626c5794a3fc7a": "Aggiungi utenti",
|
||||
"b0d7dd8a1b0349622d6e0c6e643e24a9ea0efa1d": "Modifica ruolo",
|
||||
"5009630cdf32ab4f1c78737b9617b8773512c05a": "Linee:",
|
||||
"8a0bda4c47f10b2423ff183acefbf70d4ab52ea2": "Cancella i registri",
|
||||
"ccf5ea825526ac490974336cb5c24352886abc07": "Apri il file",
|
||||
"5656a06f17c24b2d7eae9c221567b209743829a9": "Apri il file in una nuova scheda",
|
||||
"a0720c36ee1057e5c54a86591b722485c62d7b1a": "Vai alle iscrizioni",
|
||||
"d02888c485d3aeab6de628508f4a00312a722894": "I miei video",
|
||||
"ef418d4ece7c844f3a5e431da1aa59bedd88da7b": "Parametri personalizzati generali",
|
||||
"56a2a773fbd5a6b9ac2e6b89d29d70a2ed0f3227": "Nascondi.",
|
||||
"84ffcebac2709ca0785f4a1d5ba274433b5beabc": "Conosciuto anche come Client ID.",
|
||||
"cfe829634b1144bc44b6d38cf5584ea65db9804f": "File output predefinito",
|
||||
"3d1a47dc18b7bd8b5d9e1eb44b235ed9c4a2b513": "Riscarica i nuovi contenuti",
|
||||
"13759b09a7f4074ceee8fa2f968f9815fdf63295": "A volte i nuovi video vengono scaricati prima di essere completamente elaborati. Questa impostazione significa che per i nuovi video verrà effettuata una verifica il giorno successivo per la ricerca di versioni di qualità superiore.",
|
||||
"ddc31f2885b1b33a7651963254b0c197f2a64086": "Vedi altro.",
|
||||
"24dc3ecf7ec2c2144910c4f3d38343828be03a4c": "Generato automaticamente",
|
||||
"c776eb4992b6c98f58cd89b20c1ea8ac37888521": "Seleziona un agente di download",
|
||||
"5fb1e0083c9b2a40ac8ae7dcb2618311c291b8b9": "Scarica automaticamente le Chat Twitch",
|
||||
"8ae23bc4302a479f687f4b20a84c276182e2519c": "Chiave dell'API Twitch",
|
||||
"d162f9fcd6a7187b391e004f072ab3da8377c47d": "Usa l'API Twitch",
|
||||
"04201f9d27abd7d6f58a4328ab98063ce1072006": "Categorie",
|
||||
"1148fd45287ff09955b938756bc302042bcb29c7": "Il percorso è riferito ai percorsi di download sopra. Non includere l'estensione.",
|
||||
"dad95154dcef3509b8cc705046061fd24994bbb7": "visualizzazioni",
|
||||
"792dc6a57f28a1066db283f2e736484f066005fd": "Scarica Chat Twitch",
|
||||
"e4eeb9106dbcbc91ca1ac3fb4068915998a70f37": "Aggiungi nuova regola",
|
||||
"2489eefea00931942b91f4a1ae109514b591e2e1": "Regole",
|
||||
"c3b0b86523f1d10e84a71f9b188d54913a11af3b": "Categoria in modifica",
|
||||
"07db550ae114d9faad3a0cbb68bcc16ab6cd31fc": "In pausa",
|
||||
"73423607944a694ce6f9e55cfee329681bb4d9f9": "Nessun video trovato.",
|
||||
"29376982b1205d9d6ea3d289e8e2f8e1ac2839b1": "Ordine inverso",
|
||||
"33026f57ea65cd9c8a5d917a08083f71a718933a": "Ordine normale",
|
||||
"5caadefa4143cf6766a621b0f54f91f373a1f164": "Aggiungi contenuto",
|
||||
"0cc1dec590ecd74bef71a865fb364779bc42a749": "Categoria:",
|
||||
"303e45ffae995c9817e510e38cb969e6bb3adcbf": "(In pausa)",
|
||||
"d641b8fa5ac5e85114c733b1f7de6976bd091f70": "Qualità massima"
|
||||
}
|
||||
2482
src/assets/i18n/messages.it.xlf
Normal file
2482
src/assets/i18n/messages.it.xlf
Normal file
File diff suppressed because it is too large
Load Diff
@@ -113,7 +113,7 @@
|
||||
"61b81b11aad0b9d970ece2fce18405f07eac69c2": "只需将下面的链接拖放到书签栏中。在YouTube页面上您只需单击书签即可下载视频。",
|
||||
"c505d6c5de63cc700f0aaf8a4b31fae9e18024e5": "生成“仅音频”书签",
|
||||
"d5f69691f9f05711633128b5a3db696783266b58": "额外",
|
||||
"5fab47f146b0a4b809dcebf3db9da94df6299ea1": "使用默认下载代理",
|
||||
"5fab47f146b0a4b809dcebf3db9da94df6299ea1": "使用默认下载程序",
|
||||
"ec71e08aee647ea4a71fd6b7510c54d84a797ca6": "选择下载器",
|
||||
"00e274c496b094a019f0679c3fab3945793f3335": "选择日志级别",
|
||||
"dc3d990391c944d1fbfc7cfb402f7b5e112fb3a8": "开启高级下载选项",
|
||||
@@ -124,7 +124,7 @@
|
||||
"4d13a9cd5ed3dcee0eab22cb25198d43886942be": "用户",
|
||||
"eb3d5aefff38a814b76da74371cbf02c0789a1ef": "日志",
|
||||
"52c9a103b812f258bcddc3d90a6e3f46871d25fe": "保存",
|
||||
"fe8fd36dbf5deee1d56564965787a782a66eba44": "{VAR_SELECT, select, true {关} false {取消} }",
|
||||
"fe8fd36dbf5deee1d56564965787a782a66eba44": "{VAR_SELECT, select, true {关} false {取消} other {其他} }",
|
||||
"cec82c0a545f37420d55a9b6c45c20546e82f94e": "关于 YoutubeDL-Material",
|
||||
"199c17e5d6a419313af3c325f06dcbb9645ca618": "是根据Google的Material Design规范构建的开源YouTube下载器。您可以将喜欢的视频下载为视频或音频文件,并且可以订阅喜欢的频道和播放列表,以便及时下载他们的新视频。",
|
||||
"bc0ad0ee6630acb7fcb7802ec79f5a0ee943c1a7": "包含很多很棒的功能!支持API,Docker和本地化。在Github上查找所有受支持的功能。",
|
||||
@@ -238,5 +238,32 @@
|
||||
"cfa67d14d84fe0e9fadf251dc51ffc181173b662": "搜索起点",
|
||||
"544e09cdc99a8978f48521d45f62db0da6dcf742": "使用角色预设",
|
||||
"3697f8583ea42868aa269489ad366103d94aece7": "编辑中",
|
||||
"fb35145bfb84521e21b6385363d59221f436a573": "取消所有下载"
|
||||
"fb35145bfb84521e21b6385363d59221f436a573": "取消所有下载",
|
||||
"56a2a773fbd5a6b9ac2e6b89d29d70a2ed0f3227": "查看更少",
|
||||
"c776eb4992b6c98f58cd89b20c1ea8ac37888521": "选择一个下载程序",
|
||||
"d641b8fa5ac5e85114c733b1f7de6976bd091f70": "最高画质",
|
||||
"ddc31f2885b1b33a7651963254b0c197f2a64086": "查看更多...",
|
||||
"5fb1e0083c9b2a40ac8ae7dcb2618311c291b8b9": "自动下载Twitch弹幕",
|
||||
"84ffcebac2709ca0785f4a1d5ba274433b5beabc": "也称为客户ID",
|
||||
"8ae23bc4302a479f687f4b20a84c276182e2519c": "Twitch API 密钥",
|
||||
"d162f9fcd6a7187b391e004f072ab3da8377c47d": "使用Twitch API",
|
||||
"04201f9d27abd7d6f58a4328ab98063ce1072006": "分类",
|
||||
"ef418d4ece7c844f3a5e431da1aa59bedd88da7b": "全局自定义变量",
|
||||
"1148fd45287ff09955b938756bc302042bcb29c7": "路径相对于上述下载路径,不包括扩展名。",
|
||||
"cfe829634b1144bc44b6d38cf5584ea65db9804f": "默认输出文件夹",
|
||||
"3d1a47dc18b7bd8b5d9e1eb44b235ed9c4a2b513": "重新下载新上传的内容",
|
||||
"13759b09a7f4074ceee8fa2f968f9815fdf63295": "有时新视频会在完全处理前下载。这项设置指新视频会在第二天检查视频是否有更高画质。",
|
||||
"dad95154dcef3509b8cc705046061fd24994bbb7": "浏览",
|
||||
"792dc6a57f28a1066db283f2e736484f066005fd": "下载Twitch弹幕",
|
||||
"e4eeb9106dbcbc91ca1ac3fb4068915998a70f37": "添加新规则",
|
||||
"2489eefea00931942b91f4a1ae109514b591e2e1": "规则",
|
||||
"c3b0b86523f1d10e84a71f9b188d54913a11af3b": "编辑类别",
|
||||
"07db550ae114d9faad3a0cbb68bcc16ab6cd31fc": "暂停",
|
||||
"73423607944a694ce6f9e55cfee329681bb4d9f9": "找不到视频",
|
||||
"29376982b1205d9d6ea3d289e8e2f8e1ac2839b1": "倒序",
|
||||
"33026f57ea65cd9c8a5d917a08083f71a718933a": "正序",
|
||||
"5caadefa4143cf6766a621b0f54f91f373a1f164": "添加内容",
|
||||
"0cc1dec590ecd74bef71a865fb364779bc42a749": "类别:",
|
||||
"303e45ffae995c9817e510e38cb969e6bb3adcbf": "(暂停)",
|
||||
"24dc3ecf7ec2c2144910c4f3d38343828be03a4c": "自动生成的"
|
||||
}
|
||||
2578
src/assets/i18n/messages.zh.xlf
Normal file
2578
src/assets/i18n/messages.zh.xlf
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user