Compare commits

...

40 Commits

Author SHA1 Message Date
dependabot[bot]
6187c64a0d Bump luxon from 1.28.0 to 1.28.1 in /backend
Bumps [luxon](https://github.com/moment/luxon) from 1.28.0 to 1.28.1.
- [Release notes](https://github.com/moment/luxon/releases)
- [Changelog](https://github.com/moment/luxon/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moment/luxon/compare/1.28.0...1.28.1)

---
updated-dependencies:
- dependency-name: luxon
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-10 01:05:13 +00:00
Tzahi12345
9c0a77cb6e Merge pull request #705 from weblate/weblate-youtubedl-material-ytdl-material
Translations update from Hosted Weblate
2022-07-09 21:25:07 -04:00
Azhar Pusparadhian
75915c41c7 Translated using Weblate (Indonesian)
Currently translated at 100.0% (381 of 381 strings)

Translation: YoutubeDL-Material/ytdl-material
Translate-URL: https://hosted.weblate.org/projects/youtubedl-material/ytdl-material/id/
2022-07-09 20:18:52 +02:00
Tzahi12345
fc3c179f6a Reverted #696 and updated node version to avoid 243 error 2022-07-04 20:45:39 -04:00
Tzahi12345
f3572d274c Merge pull request #696 from martadinata666/patch-2
Update entrypoint.sh
2022-07-03 17:26:48 -04:00
Dedy Martadinata S
02447e0285 Update entrypoint.sh
about gosu behaviour, it looking for local "existed" UID, i  found it become blank.
That is our issue, so updating our entrypoint to change existed user "youtube" UID and GID to match compose request, will make gosu correctly find use UID:GID as it existed.
2022-07-03 13:05:53 +07:00
Isaac Abadi
24475386f9 Merge branch 'master' of https://github.com/Tzahi12345/YoutubeDL-Material 2022-07-01 00:55:03 -04:00
Isaac Abadi
55268301f6 Removed config env vars set message if no items were set 2022-07-01 00:54:38 -04:00
Isaac Abadi
faa76abbbd Fixed issue where setting resolution in a sub would instead require that resolution to exist (#678 and #330) 2022-07-01 00:51:30 -04:00
Glassed Silver
b827f8f0cc Merge pull request #687 from Tzahi12345/add-permissions-for-tasks-manager
Add permissions for tasks manager
2022-06-30 16:34:08 +02:00
Glassed Silver
b6b61c42d4 Merge pull request #677 from weblate/weblate-youtubedl-material-ytdl-material
Translations update from Hosted Weblate
2022-06-30 16:22:45 +02:00
Glassed Silver
6af1ce4092 Merge pull request #683 from adripo/patch-1
fix: #682 install tzdata
2022-06-30 16:20:40 +02:00
Glassed Silver
303d0015c6 Merge pull request #688 from adripo/patch-2
fix: remove exposed ports for mongo
2022-06-30 16:04:45 +02:00
adripo
56db43da79 fix: remove exposed ports for mongo
exposed ports between services in the same stack is not needed
2022-06-30 13:11:21 +02:00
Isaac Abadi
64b1a9e5c0 Updated mangled translations
Improved automatic translations command
2022-06-30 01:34:52 -04:00
Isaac Abadi
48f0a700ab Paginator is now always visible to avoid case where file type filter permanently disappears 2022-06-30 01:30:06 -04:00
Isaac Abadi
768798c6b3 Fixed issue where one-off playlist downlaods would only include the first video 2022-06-30 01:29:18 -04:00
Isaac Abadi
9d1f93acfb Added translations for manage-role component 2022-06-30 00:44:11 -04:00
Isaac Abadi
077a0d8fdb Added migration to add tasks manager permission for admin role
All routes are now properly protected against logged in users w/o permissions
2022-06-30 00:43:57 -04:00
Tzahi12345
c9359f172e Merge pull request #681 from adripo/node-config-fix
fix: node-config fix environment variable
2022-06-29 23:46:45 -04:00
Tzahi12345
d6dc4756a7 Merge pull request #680 from adripo/remove-container-config-env
fix: remove write_ytdl_config
2022-06-29 23:44:50 -04:00
adripo
9bc9b17294 fix: #682 install tzdata 2022-06-29 23:52:25 +02:00
adripo
80d3580447 fix: node-config fix environment variable 2022-06-29 20:20:27 +02:00
adripo
3f15f3bcaf fix: remove env variable check 2022-06-29 19:44:13 +02:00
Tzahi12345
703848e4e5 Merge pull request #675 from FoxxMD/unknownFormatsFix
Fixed parsing expected file size for videos with no known formats
2022-06-29 11:31:32 -04:00
Sebastian Danielsson
934965720e Translated using Weblate (Swedish)
Currently translated at 31.4% (120 of 381 strings)

Translation: YoutubeDL-Material/ytdl-material
Translate-URL: https://hosted.weblate.org/projects/youtubedl-material/ytdl-material/sv/
2022-06-29 17:17:36 +02:00
AbsurdUsername
bb4a882d19 Translated using Weblate (Italian)
Currently translated at 100.0% (381 of 381 strings)

Translation: YoutubeDL-Material/ytdl-material
Translate-URL: https://hosted.weblate.org/projects/youtubedl-material/ytdl-material/it/
2022-06-29 17:17:36 +02:00
FoxxMD
74315b8c76 Fixed parsing expected file size for videos with no known formats 2022-06-29 09:37:26 -04:00
Glassed Silver
a9e95c5bb8 Merge pull request #672 from weblate/weblate-youtubedl-material-ytdl-material
Translations update from Hosted Weblate
2022-06-28 07:20:04 +02:00
Sebastian
fe45a889c9 Added translation using Weblate (Swedish) 2022-06-27 22:14:59 +02:00
Isaac Abadi
e726e991cc Partially reverted fecefde3ad 2022-06-27 13:12:53 -04:00
Isaac Abadi
940267651d Hotfix for bug where a sub with missing videos would cause a crash during migration #670 2022-06-27 03:52:05 -04:00
Isaac Abadi
2dc68139f7 Streaming-only subs are now actually paused
DB transfers in any direction now generate backups and associated logs are set to info
2022-06-27 00:08:41 -04:00
Isaac Abadi
301451d021 Fixed issue where tasks would not initialize properly after migration until server restart
streaming-only subscriptions now autopause due to deprecated
2022-06-26 23:47:03 -04:00
Isaac Abadi
a7f8795e7e Fixed missing latest tag on docker-release 2022-06-26 23:10:47 -04:00
Isaac Abadi
162094a9b9 File type filter now only appears with the paginator 2022-06-26 23:06:15 -04:00
Isaac Abadi
e843b4c97f Fixed crash during docker-release action 2022-06-26 22:07:21 -04:00
Isaac Abadi
c784091ad6 Fixed issue where docker-release action couldn't accept inputs (2) 2022-06-26 22:04:42 -04:00
Isaac Abadi
fb404d3cee Fixed issue where docker-release action couldn't accept inputs 2022-06-26 22:02:23 -04:00
Tzahi12345
68c2ee26ff Merge pull request #657 from Tzahi12345/4.3-prep
4.3 Prep
2022-06-26 21:13:51 -04:00
23 changed files with 5496 additions and 1310 deletions

View File

@@ -36,7 +36,7 @@ jobs:
- name: Set image tag
id: tags
run: |
if [ ${{ github.event.action }} == "workflow_dispatch" ]; then
if [ "${{ github.event.inputs.tags }}" != "" ]; then
echo "::set-output name=tags::${{ github.event.inputs.tags }}"
elif [ ${{ github.event.action }} == "release" ]; then
echo "::set-output name=tags::${{ github.event.release.tag_name }}"
@@ -53,8 +53,8 @@ jobs:
${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO }}
ghcr.io/${{ github.repository_owner }}/${{ secrets.DOCKERHUB_REPO }}
tags: |
raw=${{ steps.tags.outputs.tags }}
raw=latest
type=raw,value=${{ steps.tags.outputs.tags }}
type=raw,value=latest
- name: setup platform emulator
uses: docker/setup-qemu-action@v1

View File

@@ -6,21 +6,23 @@ COPY ffmpeg-fetch.sh .
RUN sh ./ffmpeg-fetch.sh
# Create our Ubuntu 22.04 with node 16
# Create our Ubuntu 22.04 with node 16.14.2 (that specific version is required as per: https://stackoverflow.com/a/72855258/8088021)
# Go to 20.04
FROM ubuntu:20.04 AS base
ENV DEBIAN_FRONTEND=noninteractive
ARG DEBIAN_FRONTEND=noninteractive
ENV UID=1000
ENV GID=1000
ENV USER=youtube
ENV NO_UPDATE_NOTIFIER=true
ENV PM2_HOME=/app/pm2
ENV ALLOW_CONFIG_MUTATIONS=true
RUN groupadd -g $GID $USER && useradd --system -m -g $USER --uid $UID $USER && \
apt update && \
apt install -y --no-install-recommends curl ca-certificates && \
apt install -y --no-install-recommends curl ca-certificates tzdata && \
curl -fsSL https://deb.nodesource.com/setup_16.x | bash - && \
apt install -y --no-install-recommends nodejs && \
npm -g install npm && \
npm -g install npm n && \
n 16.14.2 && \
apt clean && \
rm -rf /var/lib/apt/lists/*
@@ -47,7 +49,8 @@ RUN npm config set strict-ssl false && \
# Final image
FROM base
RUN apt update && \
RUN npm install -g pm2 && \
apt update && \
apt install -y --no-install-recommends gosu python3-minimal python-is-python3 python3-pip atomicparsley && \
apt clean && \
rm -rf /var/lib/apt/lists/*

View File

@@ -68,7 +68,8 @@ db.defaults(
configWriteFlag: false,
downloads: {},
subscriptions: [],
files_to_db_migration_complete: false
files_to_db_migration_complete: false,
tasks_manager_role_migration_complete: false
}).write();
users_db.defaults(
@@ -147,16 +148,11 @@ if (fs.existsSync('version.json')) {
// don't overwrite config if it already happened.. NOT
// let alreadyWritten = db.get('configWriteFlag').value();
let writeConfigMode = process.env.write_ytdl_config;
// checks if config exists, if not, a config is auto generated
config_api.configExistsCheck();
if (writeConfigMode) {
setAndLoadConfig();
} else {
loadConfig();
}
setAndLoadConfig();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
@@ -187,13 +183,22 @@ async function checkMigrations() {
if (!new_db_system_migration_complete) {
logger.info('Beginning migration: 4.2->4.3+')
let success = await db_api.importJSONToDB(db.value(), users_db.value());
await tasks_api.setupTasks(); // necessary as tasks were not properly initialized at first
// sets migration to complete
db.set('new_db_system_migration_complete', true).write();
if (success) { logger.info('4.2->4.3+ migration complete!'); }
else { logger.error('Migration failed: 4.2->4.3+'); }
}
const tasks_manager_role_migration_complete = db.get('tasks_manager_role_migration_complete').value();
if (!tasks_manager_role_migration_complete) {
logger.info('Checking if tasks manager role permissions exist for admin user...');
const success = await auth_api.changeRolePermissions('admin', 'tasks_manager', 'yes');
if (success) logger.info('Task manager permissions check complete!');
else logger.error('Failed to auto add tasks manager permissions to admin role!');
db.set('tasks_manager_role_migration_complete', true).write();
}
return true;
}
@@ -483,8 +488,9 @@ async function setAndLoadConfig() {
}
async function setConfigFromEnv() {
let config_items = getEnvConfigItems();
let success = config_api.setConfigItems(config_items);
const config_items = getEnvConfigItems();
if (!config_items || config_items.length === 0) return true;
const success = config_api.setConfigItems(config_items);
if (success) {
logger.info('Config items set using ENV variables.');
await utils.wait(100);
@@ -575,7 +581,11 @@ async function watchSubscriptions() {
if (!subscriptions) return;
const valid_subscriptions = subscriptions.filter(sub => !sub.paused);
// auto pause deprecated streamingOnly mode
const streaming_only_subs = subscriptions.filter(sub => sub.streamingOnly);
subscriptions_api.updateSubscriptionPropertyMultiple(streaming_only_subs, {paused: true});
const valid_subscriptions = subscriptions.filter(sub => !sub.paused && !sub.streamingOnly);
let subscriptions_amount = valid_subscriptions.length;
let delay_interval = calculateSubcriptionRetrievalDelay(subscriptions_amount);

View File

@@ -361,7 +361,6 @@ exports.userHasPermission = async function(user_uid, permission) {
logger.error('Invalid role ' + role);
return false;
}
const role_permissions = (await db_api.getRecords('roles'))['permissions'];
const user_has_explicit_permission = user_obj['permissions'].includes(permission);
const permission_in_overrides = user_obj['permission_overrides'].includes(permission);
@@ -376,7 +375,8 @@ exports.userHasPermission = async function(user_uid, permission) {
}
// no overrides, let's check if the role has the permission
if (role_permissions.includes(permission)) {
const role_has_permission = await exports.roleHasPermissions(role, permission);
if (role_has_permission) {
return true;
} else {
logger.verbose(`User ${user_uid} failed to get permission ${permission}`);
@@ -384,6 +384,16 @@ exports.userHasPermission = async function(user_uid, permission) {
}
}
exports.roleHasPermissions = async function(role, permission) {
const role_obj = await db_api.getRecord('roles', {key: role})
if (!role) {
logger.error(`Role ${role} does not exist!`);
}
const role_permissions = role_obj['permissions'];
if (role_permissions && role_permissions.includes(permission)) return true;
else return false;
}
exports.userPermissions = async function(user_uid) {
let user_permissions = [];
const user_obj = await db_api.getRecord('users', ({uid: user_uid}));

View File

@@ -221,7 +221,8 @@ exports.AVAILABLE_PERMISSIONS = [
'subscriptions',
'sharing',
'advanced_download',
'downloads_manager'
'downloads_manager',
'tasks_manager'
];
exports.DETAILS_BIN_PATH = 'node_modules/youtube-dl/bin/details'

View File

@@ -198,7 +198,7 @@ async function registerFileDBManual(file_object) {
path_object = path.parse(file_object['path']);
file_object['path'] = path.format(path_object);
exports.insertRecordIntoTable('files', file_object, {path: file_object['path']})
await exports.insertRecordIntoTable('files', file_object, {path: file_object['path']})
return file_object;
}
@@ -925,6 +925,7 @@ exports.importJSONToDB = async (db_json, users_json) => {
const createFilesRecords = (files, subscriptions) => {
for (let i = 0; i < subscriptions.length; i++) {
const subscription = subscriptions[i];
if (!subscription['videos']) continue;
subscription['videos'] = subscription['videos'].map(file => ({ ...file, sub_id: subscription['id'], user_uid: subscription['user_uid'] ? subscription['user_uid'] : undefined}));
files = files.concat(subscriptions[i]['videos']);
}
@@ -985,7 +986,7 @@ exports.backupDB = async () => {
const backup_file_name = `${using_local_db ? 'local' : 'remote'}_db.json.${Date.now()/1000}.bak`;
const path_to_backups = path.join(backup_dir, backup_file_name);
logger.verbose(`Backing up ${using_local_db ? 'local' : 'remote'} DB to ${path_to_backups}`);
logger.info(`Backing up ${using_local_db ? 'local' : 'remote'} DB to ${path_to_backups}`);
const table_to_records = {};
for (let i = 0; i < tables_list.length; i++) {
@@ -1032,10 +1033,11 @@ exports.transferDB = async (local_to_remote) => {
table_to_records[table] = await exports.getRecords(table);
}
logger.info('Backup up DB...');
await exports.backupDB(); // should backup always
using_local_db = !local_to_remote;
if (local_to_remote) {
logger.debug('Backup up DB...');
await exports.backupDB();
const db_connected = await exports.connectToDB(5, true);
if (!db_connected) {
logger.error('Failed to transfer database - could not connect to MongoDB. Verify that your connection URL is valid.');

View File

@@ -203,6 +203,7 @@ async function collectInfo(download_uid) {
options.customOutput = category['custom_output'];
options.noRelativePath = true;
args = await exports.generateArgs(url, type, options, download['user_uid']);
args = utils.filterArgs(args, ['--no-simulate']);
info = await exports.getVideoInfoByURL(url, args, download_uid);
}
@@ -356,7 +357,7 @@ async function downloadQueuedFile(download_uid) {
if (file_objs.length > 1) {
// create playlist
const playlist_name = file_objs.map(file_obj => file_obj.title).join(', ');
container = await db_api.createPlaylist(playlist_name, file_objs.map(file_obj => file_obj.uid), type, download['user_uid']);
container = await db_api.createPlaylist(playlist_name, file_objs.map(file_obj => file_obj.uid), download['user_uid']);
} else if (file_objs.length === 1) {
container = file_objs[0];
} else {
@@ -402,6 +403,8 @@ exports.generateArgs = async (url, type, options, user_uid = null, simulated = f
// video-specific args
const selectedHeight = options.selectedHeight;
const maxHeight = options.maxHeight;
const heightParam = selectedHeight || maxHeight;
// audio-specific args
const maxBitrate = options.maxBitrate;
@@ -422,8 +425,8 @@ exports.generateArgs = async (url, type, options, user_uid = null, simulated = f
} else {
if (customQualityConfiguration) {
qualityPath = ['-f', customQualityConfiguration, '--merge-output-format', 'mp4'];
} else if (selectedHeight && selectedHeight !== '' && !is_audio) {
qualityPath = ['-f', `'(mp4)[height=${selectedHeight}]`];
} else if (heightParam && heightParam !== '' && !is_audio) {
qualityPath = ['-f', `'(mp4)[height${maxHeight ? '<' : ''}=${heightParam}]`];
} else if (is_audio) {
qualityPath = ['--audio-quality', maxBitrate ? maxBitrate : '0']
}
@@ -506,7 +509,10 @@ exports.generateArgs = async (url, type, options, user_uid = null, simulated = f
}
if (default_downloader === 'yt-dlp') {
downloadConfig.push('--no-clean-infojson');
downloadConfig = utils.filterArgs(downloadConfig, ['--print-json']);
// in yt-dlp -j --no-simulate is preferable
downloadConfig.push('--no-clean-info-json', '-j', '--no-simulate');
}
}

1235
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -45,7 +45,6 @@
"passport-jwt": "^4.0.0",
"passport-ldapauth": "^3.0.1",
"passport-local": "^1.0.0",
"pm2": "^5.2.0",
"progress": "^2.0.3",
"ps-node": "^0.1.6",
"read-last-lines": "^1.7.2",

View File

@@ -313,7 +313,7 @@ function generateOptionsForSubscriptionDownload(sub, user_uid) {
let default_output = config_api.getConfigItem('ytdl_default_file_output') ? config_api.getConfigItem('ytdl_default_file_output') : '%(title)s';
const base_download_options = {
selectedHeight: sub.maxQuality && sub.maxQuality !== 'best' ? sub.maxQuality : null,
maxHeight: sub.maxQuality && sub.maxQuality !== 'best' ? sub.maxQuality : null,
customFileFolderPath: getAppendedBasePath(sub, basePath),
customOutput: sub.custom_output ? `${sub.custom_output}` : `${default_output}`,
customArchivePath: path.join(basePath, 'archives', sub.name),
@@ -407,7 +407,7 @@ async function generateArgsForSubscription(sub, user_uid, redownload = false, de
const default_downloader = utils.getCurrentDownloader() || config_api.getConfigItem('ytdl_default_downloader');
if (default_downloader === 'yt-dlp') {
downloadConfig.push('--no-clean-infojson');
downloadConfig.push('--no-clean-info-json');
}
downloadConfig = utils.filterArgs(downloadConfig, ['--write-comments']);
@@ -458,7 +458,7 @@ async function updateSubscription(sub) {
async function updateSubscriptionPropertyMultiple(subs, assignment_obj) {
subs.forEach(async sub => {
await updateSubscriptionProperty(sub, assignment_obj, sub.user_uid);
await updateSubscriptionProperty(sub, assignment_obj);
});
}

View File

@@ -172,11 +172,13 @@ function getExpectedFileSize(input_info_jsons) {
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 || available_format.filesize_approx)) {
if (info_json.formats !== undefined) {
info_json.formats.forEach(available_format => {
if (available_format.format_id === format_id && (available_format.filesize || available_format.filesize_approx)) {
individual_expected_filesize += (available_format.filesize ? available_format.filesize : available_format.filesize_approx);
}
});
}
});
}
});
expected_filesize += individual_expected_filesize;
});
@@ -210,7 +212,7 @@ function deleteJSONFile(file_path, type) {
const ext = type === 'audio' ? '.mp3' : '.mp4';
const file_path_no_extension = removeFileExtension(file_path);
let json_path = file_path_no_extension + '.info.json';
let alternate_json_path = file_path_no_extension + ext + '.info.json';
@@ -331,9 +333,9 @@ function createEdgeNGrams(str) {
if (str && str.length > 3) {
const minGram = 3
const maxGram = str.length
return str.split(" ").reduce((ngrams, token) => {
if (token.length > minGram) {
if (token.length > minGram) {
for (let i = minGram; i <= maxGram && i <= token.length; ++i) {
ngrams = [...ngrams, token.substr(0, i)]
}
@@ -343,7 +345,7 @@ function createEdgeNGrams(str) {
return ngrams
}, []).join(" ")
}
return str
}
@@ -459,7 +461,7 @@ function injectArgs(original_args, new_args) {
for (let i = 0; i < new_args.length; i++) {
const new_arg = new_args[i];
if (!new_arg.startsWith('-') && !new_arg.startsWith('--') && i > 0 && original_args.includes(new_args[i - 1])) continue;
if (CONSTS.YTDL_ARGS_WITH_VALUES.has(new_arg)) {
if (original_args.includes(new_arg)) {
const original_index = original_args.indexOf(new_arg);

View File

@@ -2,7 +2,6 @@ version: "2"
services:
ytdl_material:
environment:
ALLOW_CONFIG_MUTATIONS: 'true'
ytdl_mongodb_connection_string: 'mongodb://ytdl-mongo-db:27017'
ytdl_use_local_db: 'false'
write_ytdl_config: 'true'
@@ -20,11 +19,9 @@ services:
image: tzahi12345/youtubedl-material:latest
ytdl-mongo-db:
image: mongo
ports:
- "27017:27017"
logging:
driver: "none"
container_name: mongo-db
restart: always
volumes:
- ./db/:/data/db
- ./db/:/data/db

View File

@@ -13,7 +13,7 @@
"e2e": "ng e2e",
"electron": "ng build --base-href ./ && electron .",
"generate": "openapi --input ./\"Public API v1.yaml\" --output ./src/api-types --exportCore false --exportServices false --exportModels true",
"i18n-source": "ng extract-i18n --output-path=src/assets/i18n"
"i18n-source": "ng extract-i18n --output-path=src/assets/i18n --out-file=messages.en.xlf"
},
"engines": {
"node": "12.3.1",

View File

@@ -44,7 +44,7 @@
<a *ngIf="postsService.config && postsService.config.Advanced.multi_user_mode && !postsService.isLoggedIn" mat-list-item (click)="sidenav.close()" routerLink='/login'><ng-container i18n="Navigation menu Login Page title">Login</ng-container></a>
<a *ngIf="postsService.config && allowSubscriptions && postsService.hasPermission('subscriptions')" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/subscriptions'><ng-container i18n="Navigation menu Subscriptions Page title">Subscriptions</ng-container></a>
<a *ngIf="postsService.config && enableDownloadsManager && postsService.hasPermission('downloads_manager')" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/downloads'><ng-container i18n="Navigation menu Downloads Page title">Downloads</ng-container></a>
<a *ngIf="postsService.config && enableDownloadsManager && postsService.hasPermission('downloads_manager')" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/tasks'><ng-container i18n="Navigation menu Tasks Page title">Tasks</ng-container></a>
<a *ngIf="postsService.config && postsService.hasPermission('tasks_manager')" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/tasks'><ng-container i18n="Navigation menu Tasks Page title">Tasks</ng-container></a>
<ng-container *ngIf="postsService.config && postsService.hasPermission('settings')">
<mat-divider></mat-divider>
<a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/settings'><ng-container i18n="Settings menu label">Settings</ng-container></a>

View File

@@ -53,9 +53,9 @@ export class CustomPlaylistsComponent implements OnInit {
dialogRef.afterClosed().subscribe(result => {
if (result) {
this.getAllPlaylists();
this.postsService.openSnackBar($localize`Successfully created playlist!', '`);
this.postsService.openSnackBar($localize`Successfully created playlist!`);
} else if (result === false) {
this.postsService.openSnackBar($localize`ERROR: failed to create playlist!', '`);
this.postsService.openSnackBar($localize`ERROR: failed to create playlist!`);
}
});
}
@@ -75,6 +75,7 @@ export class CustomPlaylistsComponent implements OnInit {
}
} else {
// playlist not found
// TODO: Make translatable
console.error(`Playlist with ID ${playlistID} not found!`);
}
}
@@ -96,7 +97,7 @@ export class CustomPlaylistsComponent implements OnInit {
this.postsService.removePlaylist(playlistID).subscribe(res => {
if (res['success']) {
this.playlists.splice(index, 1);
this.postsService.openSnackBar($localize`Playlist successfully removed.', '`);
this.postsService.openSnackBar($localize`Playlist successfully removed.`);
}
this.getAllPlaylists();
});

View File

@@ -14,12 +14,13 @@ export class ManageRoleComponent implements OnInit {
permissions = null;
permissionToLabel = {
'filemanager': 'File manager',
'settings': 'Settings access',
'subscriptions': 'Subscriptions',
'sharing': 'Share files',
'advanced_download': 'Use advanced download mode',
'downloads_manager': 'Use downloads manager'
'filemanager': $localize`File manager`,
'settings': $localize`Settings access`,
'subscriptions': $localize`Subscriptions`,
'sharing': $localize`Share files`,
'advanced_download': $localize`Use advanced download mode`,
'downloads_manager': $localize`Use downloads manager`,
'tasks_manager': $localize`Use tasks manager`,
}
constructor(public postsService: PostsService, private dialogRef: MatDialogRef<ManageRoleComponent>,

View File

@@ -106,7 +106,7 @@
</mat-select>
</mat-form-field>
</div>
<mat-paginator class="paginator" #paginator *ngIf="paged_data && paged_data.length > 0" (page)="pageChangeEvent($event)" [length]="file_count"
<mat-paginator class="paginator" #paginator (page)="pageChangeEvent($event)" [length]="file_count"
[pageSize]="pageSize"
[pageSizeOptions]="[5, 10, 25, 100, this.paged_data && this.paged_data.length > 100 ? this.paged_data.length : 250]">
</mat-paginator>

View File

@@ -306,7 +306,7 @@ export class MainComponent implements OnInit {
this.downloadingfile = false;
if (!this.autoplay && !this.downloadOnlyMode && !navigate_mode) {
// do nothing
this.reloadRecentVideos();
this.reloadRecentVideos(is_playlist);
} else {
// if download only mode, just download the file. no redirect
if (force_view === false && this.downloadOnlyMode && !this.iOS) {
@@ -315,7 +315,7 @@ export class MainComponent implements OnInit {
} else {
this.downloadFileFromServer(container as DatabaseFile, type);
}
this.reloadRecentVideos();
this.reloadRecentVideos(is_playlist);
} else {
localStorage.setItem('player_navigator', this.router.url.split(';')[0]);
if (is_playlist) {
@@ -780,8 +780,9 @@ export class MainComponent implements OnInit {
});
}
reloadRecentVideos(): void {
reloadRecentVideos(is_playlist = false): void {
this.postsService.files_changed.next(true);
if (is_playlist) this.postsService.playlists_changed.next(true);
}
getURLArray(url_str: string): Array<string> {

View File

@@ -4,7 +4,7 @@ import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import { THEMES_CONFIG } from '../themes';
import { Router, CanActivate } from '@angular/router';
import { Router, CanActivate, ActivatedRouteSnapshot } from '@angular/router';
import { DOCUMENT } from '@angular/common';
import { BehaviorSubject } from 'rxjs';
import { MatSnackBar } from '@angular/material/snack-bar';
@@ -229,12 +229,15 @@ export class PostsService implements CanActivate {
}
}
canActivate(route, state): Promise<boolean> {
return new Promise(resolve => {
resolve(true);
})
console.log(route);
throw new Error('Method not implemented.');
canActivate(route: ActivatedRouteSnapshot, state): Promise<boolean> {
const PATH_TO_REQUIRED_PERM = {
settings: 'settings',
subscriptions: 'subscriptions',
downloads: 'downloads_manager',
tasks: 'tasks_manager'
}
const required_perm = PATH_TO_REQUIRED_PERM[route.routeConfig.path];
return required_perm ? this.hasPermission(required_perm) : true;
}
setTheme(theme) {

View File

@@ -94,25 +94,25 @@
</context-group>
<note priority="1" from="description">Settings menu label</note>
</trans-unit>
<trans-unit id="65918861159071115" datatype="html">
<source>Successfully created playlist!&apos;, &apos;</source>
<trans-unit id="39921032161993566" datatype="html">
<source>Successfully created playlist!</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/custom-playlists/custom-playlists.component.ts</context>
<context context-type="linenumber">56</context>
</context-group>
</trans-unit>
<trans-unit id="7241816854520039909" datatype="html">
<source>ERROR: failed to create playlist!&apos;, &apos;</source>
<trans-unit id="2070856663109337061" datatype="html">
<source>ERROR: failed to create playlist!</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/custom-playlists/custom-playlists.component.ts</context>
<context context-type="linenumber">58</context>
</context-group>
</trans-unit>
<trans-unit id="5070854963159885174" datatype="html">
<source>Playlist successfully removed.&apos;, &apos;</source>
<trans-unit id="820184305380634591" datatype="html">
<source>Playlist successfully removed.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/custom-playlists/custom-playlists.component.ts</context>
<context context-type="linenumber">99</context>
<context context-type="linenumber">100</context>
</context-group>
</trans-unit>
<trans-unit id="2f933b826a570836cab04f683970a2d22068458c" datatype="html">
@@ -645,6 +645,55 @@
</context-group>
<note priority="1" from="description">Close</note>
</trans-unit>
<trans-unit id="4250042184551786923" datatype="html">
<source>File manager</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">17</context>
</context-group>
</trans-unit>
<trans-unit id="8525406024045742391" datatype="html">
<source>Settings access</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">18</context>
</context-group>
</trans-unit>
<trans-unit id="1812379335568847528" datatype="html">
<source>Subscriptions</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">19</context>
</context-group>
</trans-unit>
<trans-unit id="4744991787069301975" datatype="html">
<source>Share files</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">20</context>
</context-group>
</trans-unit>
<trans-unit id="5029464708330583545" datatype="html">
<source>Use advanced download mode</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">21</context>
</context-group>
</trans-unit>
<trans-unit id="4278268519633335280" datatype="html">
<source>Use downloads manager</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">22</context>
</context-group>
</trans-unit>
<trans-unit id="3426988753455920032" datatype="html">
<source>Use tasks manager</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">23</context>
</context-group>
</trans-unit>
<trans-unit id="2bd201aea09e43fbfd3cd15ec0499b6755302329" datatype="html">
<source>Manage user</source>
<context-group purpose="location">

File diff suppressed because it is too large Load Diff

View File

@@ -3332,7 +3332,7 @@
</trans-unit>
<trans-unit id="b1c08387975e6feada407c9b5f5f564261b8192b" datatype="html">
<source>Database information could not be retrieved. Check the server logs for more information.</source>
<target state="translated">Impossibile recuperare le informazioni del database. Controllare i registri del server per ulteriori informazioni.</target>
<target state="translated">Impossibile recuperare le informazioni del database. Controllare il registro del server per ulteriori informazioni.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">333</context>
@@ -3347,6 +3347,698 @@
<context context-type="linenumber">48</context>
</context-group>
</trans-unit>
<trans-unit id="5070854963159885174" datatype="html">
<source>Playlist successfully removed.', '</source>
<target state="translated">Playlist rimossa con successo.', '</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/custom-playlists/custom-playlists.component.ts</context>
<context context-type="linenumber">99</context>
</context-group>
</trans-unit>
<trans-unit id="5215119607776782829" datatype="html">
<source>Select downloads to clear</source>
<target state="translated">Seleziona i download da cancellare</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">132</context>
</context-group>
</trans-unit>
<trans-unit id="2723988842145709249" datatype="html">
<source>Errored downloads</source>
<target state="translated">Download errati</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">146</context>
</context-group>
</trans-unit>
<trans-unit id="3961621815065792326" datatype="html">
<source>Failed to clear finished downloads!</source>
<target state="translated">Cancellazione dei download finiti non riuscita!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">157</context>
</context-group>
</trans-unit>
<trans-unit id="5823550543348347814" datatype="html">
<source>Cleared downloads!</source>
<target state="translated">Download cancellati!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">159</context>
</context-group>
</trans-unit>
<trans-unit id="2293081271355999967" datatype="html">
<source>Logs successfully cleared!</source>
<target state="translated">Registro cancellato con successo!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/logs-viewer/logs-viewer.component.ts</context>
<context context-type="linenumber">75</context>
</context-group>
</trans-unit>
<trans-unit id="4516710756538206828" datatype="html">
<source>Failed to clear logs!</source>
<target state="translated">Impossibile cancellare il registro!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/logs-viewer/logs-viewer.component.ts</context>
<context context-type="linenumber">77</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/logs-viewer/logs-viewer.component.ts</context>
<context context-type="linenumber">80</context>
</context-group>
</trans-unit>
<trans-unit id="52e0fa8ada52c3f29774a4508582fd98250b9f93" datatype="html">
<source>My files</source>
<target state="translated">I miei file</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.html</context>
<context context-type="linenumber">20</context>
</context-group>
<note priority="1" from="description">My files title</note>
</trans-unit>
<trans-unit id="6827066f436adfc56a142d5816a8be6113d73b01" datatype="html">
<source>No files found.</source>
<target state="translated">Nessun file trovato.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.html</context>
<context context-type="linenumber">40</context>
</context-group>
<note priority="1" from="description">No files found</note>
</trans-unit>
<trans-unit id="5704ec2049d007c5f5fb495a5d8b607e68d58081" datatype="html">
<source>Order</source>
<target state="translated">Ordina</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.html</context>
<context context-type="linenumber">53</context>
</context-group>
<note priority="1" from="description">Order</note>
</trans-unit>
<trans-unit id="ae9a5141f5c6bd62cee4ce837598ea8b0904e5cf" datatype="html">
<source>Select files</source>
<target state="translated">Seleziona file</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.html</context>
<context context-type="linenumber">71</context>
</context-group>
<note priority="1" from="description">Select files</note>
</trans-unit>
<trans-unit id="2159130950882492111" datatype="html">
<source>Cancel</source>
<target state="translated">Annulla</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/confirm-dialog/confirm-dialog.component.ts</context>
<context context-type="linenumber">15</context>
</context-group>
</trans-unit>
<trans-unit id="1709839462010459086" datatype="html">
<source>Cookies successfully uploaded!</source>
<target state="translated">Cookie caricati con successo!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component.ts</context>
<context context-type="linenumber">42</context>
</context-group>
</trans-unit>
<trans-unit id="3480433876298276350" datatype="html">
<source>Database successfully restored!</source>
<target state="translated">Database ripristinato con successo!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/restore-db-dialog/restore-db-dialog.component.ts</context>
<context context-type="linenumber">39</context>
</context-group>
</trans-unit>
<trans-unit id="1946323844380374711" datatype="html">
<source>Failed to restore database! See browser console for more info.</source>
<target state="translated">Ripristino del database non riuscito! Guarda la console del browser per più informazioni.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/restore-db-dialog/restore-db-dialog.component.ts</context>
<context context-type="linenumber">46</context>
</context-group>
</trans-unit>
<trans-unit id="4960870191807928282" datatype="html">
<source>Sharing enabled.</source>
<target state="translated">Condivisione abilitata.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/share-media-dialog/share-media-dialog.component.ts</context>
<context context-type="linenumber">68</context>
</context-group>
</trans-unit>
<trans-unit id="2720327817780634026" datatype="html">
<source>Failed to enable sharing.</source>
<target state="translated">Abilitazione della condivisione non riuscita.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/share-media-dialog/share-media-dialog.component.ts</context>
<context context-type="linenumber">71</context>
</context-group>
</trans-unit>
<trans-unit id="5397815846940616259" datatype="html">
<source>You must specify an amount of time</source>
<target state="translated">Devi specificare un periodo di tempo</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/subscribe-dialog/subscribe-dialog.component.ts</context>
<context context-type="linenumber">79</context>
</context-group>
</trans-unit>
<trans-unit id="3544790314111256717" datatype="html">
<source>ERROR:</source>
<target state="translated">ERRORE:</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/subscribe-dialog/subscribe-dialog.component.ts</context>
<context context-type="linenumber">95</context>
</context-group>
</trans-unit>
<trans-unit id="f4003b626fcbf3a871778d4dba166e109d02f87c" datatype="html">
<source>Thumbnail URL</source>
<target state="translated">URL della miniatura</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/video-info-dialog/video-info-dialog.component.html</context>
<context context-type="linenumber">29</context>
</context-group>
<note priority="1" from="description">Thumbnail URL</note>
</trans-unit>
<trans-unit id="607de17c2a755f65775881c19e276e7c933bcf94" datatype="html">
<source>Category</source>
<target state="translated">Categoria</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/video-info-dialog/video-info-dialog.component.html</context>
<context context-type="linenumber">32</context>
</context-group>
<note priority="1" from="description">Category</note>
</trans-unit>
<trans-unit id="3f741a2c015bb728088b630296ca401e823c6af8" datatype="html">
<source>View count</source>
<target state="translated">Numero di visualizzazioni</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/video-info-dialog/video-info-dialog.component.html</context>
<context context-type="linenumber">42</context>
</context-group>
<note priority="1" from="description">View count</note>
</trans-unit>
<trans-unit id="989f5aa799ee9672675d68109bff29d1d88ebd49" datatype="html">
<source>Local view count</source>
<target state="translated">Numero di visualizzazioni locale</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/video-info-dialog/video-info-dialog.component.html</context>
<context context-type="linenumber">45</context>
</context-group>
<note priority="1" from="description">Local view count</note>
</trans-unit>
<trans-unit id="9fa37704969eeebd496a172c5077370f569df3ae" datatype="html">
<source>Resolution:</source>
<target state="translated">Risoluzione:</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/video-info-dialog/video-info-dialog.component.html</context>
<context context-type="linenumber">51</context>
</context-group>
<note priority="1" from="description">Video resolution property</note>
</trans-unit>
<trans-unit id="9fc54db2830fbbd332b1adebe28e9283069107ef" datatype="html">
<source>Audio bitrate:</source>
<target state="translated">Bitrate audio:</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/video-info-dialog/video-info-dialog.component.html</context>
<context context-type="linenumber">55</context>
</context-group>
<note priority="1" from="description">Video audio bitrate property</note>
</trans-unit>
<trans-unit id="2734512985872312443" datatype="html">
<source>Failed to get file information from the server.</source>
<target state="translated">Impossibile ottenere le informazioni del file dal server.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/player/player.component.ts</context>
<context context-type="linenumber">149</context>
</context-group>
</trans-unit>
<trans-unit id="5d78fe9ba69a8710613d3f7c35b22e9c8226e4dc" datatype="html">
<source>Twitch Client ID</source>
<target state="translated">ID Client Twitch</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">266</context>
</context-group>
<note priority="1" from="description">Twitch Client ID setting placeholder</note>
</trans-unit>
<trans-unit id="9208873922277364009" datatype="html">
<source>Failed to update categories!</source>
<target state="translated">Aggiornamento delle categorie non riuscito!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">134</context>
</context-group>
</trans-unit>
<trans-unit id="7180231139026789468" datatype="html">
<source>Language successfully changed! Reload to update the page.</source>
<target state="translated">Lingua cambiata con successo! Ricarica per aggiornare la pagina.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">209</context>
</context-group>
</trans-unit>
<trans-unit id="6123898845299902958" datatype="html">
<source>Successfully transfered DB! Reloading info...</source>
<target state="translated">DB trasferito con successo! Ricaricando informazioni...</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">340</context>
</context-group>
</trans-unit>
<trans-unit id="5681417617361245213" datatype="html">
<source>Failed to transfer DB -- transfer was aborted. Error:</source>
<target state="translated">Trasferimento del DB non riuscito -- transferimento interrotto. Errore:</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">343</context>
</context-group>
</trans-unit>
<trans-unit id="6519219215739537829" datatype="html">
<source>Connection failed! Error: Server error. See logs for more info.</source>
<target state="translated">Connessione non riuscita! Errore: Errore server. Guarda il registro per più informazioni.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">363</context>
</context-group>
</trans-unit>
<trans-unit id="65918861159071115" datatype="html">
<source>Successfully created playlist!', '</source>
<target state="translated">Playlist creata con successo!', '</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/custom-playlists/custom-playlists.component.ts</context>
<context context-type="linenumber">56</context>
</context-group>
</trans-unit>
<trans-unit id="7114033980971410157" datatype="html">
<source>Failed to pause download! See server logs for more info.</source>
<target state="translated">Interruzione del download non riuscita! Guarda il registro del server per più informazioni.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">170</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">218</context>
</context-group>
</trans-unit>
<trans-unit id="8348223454028662277" datatype="html">
<source>OK.</source>
<target state="translated">OK.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">270</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">273</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">276</context>
</context-group>
</trans-unit>
<trans-unit id="22bac71dbdc1ac62607135994f81cca8094cb251" datatype="html">
<source>Upload date</source>
<target state="translated">Data caricamento</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/video-info-dialog/video-info-dialog.component.html</context>
<context context-type="linenumber">20</context>
</context-group>
<note priority="1" from="description">Upload date</note>
</trans-unit>
<trans-unit id="1019978815798793544" datatype="html">
<source>Failed to retrieve logs!</source>
<target state="translated">Recupero dei registro non riuscito!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/logs-viewer/logs-viewer.component.ts</context>
<context context-type="linenumber">46</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/logs-viewer/logs-viewer.component.ts</context>
<context context-type="linenumber">51</context>
</context-group>
</trans-unit>
<trans-unit id="3481862581074838726" datatype="html">
<source>VOD url for this video is not supported. VOD ID must be after "twitch.tv/videos/"</source>
<target state="translated">L'url del VOD di questo video non è supportato. L'ID del VOD deve essere dopo "twitch.tv/videos/"</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/twitch-chat/twitch-chat.component.ts</context>
<context context-type="linenumber">99</context>
</context-group>
</trans-unit>
<trans-unit id="7241816854520039909" datatype="html">
<source>ERROR: failed to create playlist!', '</source>
<target state="translated">ERRORE: creazione della playlist non riuscita!', '</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/custom-playlists/custom-playlists.component.ts</context>
<context context-type="linenumber">58</context>
</context-group>
</trans-unit>
<trans-unit id="a4a4a5f03d7d0831ccf6774094e66a9507a42b58" datatype="html">
<source>Clear downloads</source>
<target state="translated">Cancella i download</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context>
<context context-type="linenumber">85</context>
</context-group>
<note priority="1" from="description">Clear downloads</note>
</trans-unit>
<trans-unit id="3299455901271096793" datatype="html">
<source>Clear downloads</source>
<target state="translated">Cancella i download</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">131</context>
</context-group>
</trans-unit>
<trans-unit id="4050356167294261426" datatype="html">
<source>Delete success!</source>
<target state="translated">Cancellazione avvenuta con successo!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">270</context>
</context-group>
</trans-unit>
<trans-unit id="7405156667148936748" datatype="html">
<source>Delete failed!</source>
<target state="translated">Cancellazione non riuscita!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">273</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">276</context>
</context-group>
</trans-unit>
<trans-unit id="8485375438204712002" datatype="html">
<source>Finished downloads</source>
<target state="translated">Download terminati</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">138</context>
</context-group>
</trans-unit>
<trans-unit id="5223827577229167333" datatype="html">
<source>Failed to pause all downloads! See server logs for more info.</source>
<target state="translated">Interruzione di tutti i download non riuscita! Guarda il registro del server per più informazioni.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">178</context>
</context-group>
</trans-unit>
<trans-unit id="5801924165267871854" datatype="html">
<source>Paused downloads</source>
<target state="translated">Download interrotti</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">142</context>
</context-group>
</trans-unit>
<trans-unit id="7157191502004604261" datatype="html">
<source>Chat could not be downloaded.</source>
<target state="translated">Impossibile scaricare la chat.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/twitch-chat/twitch-chat.component.ts</context>
<context context-type="linenumber">110</context>
</context-group>
</trans-unit>
<trans-unit id="87406377200084623" datatype="html">
<source>Logs copied to clipboard!</source>
<target state="translated">Registro copiato negli appunti!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/logs-viewer/logs-viewer.component.ts</context>
<context context-type="linenumber">56</context>
</context-group>
</trans-unit>
<trans-unit id="5456775416888155476" datatype="html">
<source>Failed to resume download! See server logs for more info.</source>
<target state="translated">Ripresa del download non riuscita! Guarda il registro del server per più informazioni.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">186</context>
</context-group>
</trans-unit>
<trans-unit id="6268791413935580107" datatype="html">
<source>Failed to resume all downloads! See server logs for more info.</source>
<target state="translated">Ripresa di tutti i download non riuscita! Guarda il registro del server per più informazioni.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">194</context>
</context-group>
</trans-unit>
<trans-unit id="571023367671104036" datatype="html">
<source>Failed to restart download! See server logs for more info.</source>
<target state="translated">Riavvio del download non riuscito! Guarda il registro del server per più informazioni.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">202</context>
</context-group>
</trans-unit>
<trans-unit id="4529487534884306633" datatype="html">
<source>Failed to cancel download! See server logs for more info.</source>
<target state="translated">Annullamento del download non riuscito! Guarda il registro del server per più informazioni.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">210</context>
</context-group>
</trans-unit>
<trans-unit id="c1b7e6d75ff4285c7636c67e5ef259629b81725b" datatype="html">
<source>Confirm Password</source>
<target state="translated">Conferma password</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/login/login.component.html</context>
<context context-type="linenumber">28</context>
</context-group>
<note priority="1" from="description">Confirm Password</note>
</trans-unit>
<trans-unit id="8937901770314883418" datatype="html">
<source>Successfully deleted file:</source>
<target state="translated">File eliminati con successo:</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">291</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">299</context>
</context-group>
</trans-unit>
<trans-unit id="e58f5716d6c08b6a841eb003c9f9774b5c5d34a9" datatype="html">
<source>Delete and don't download again</source>
<target state="translated">Elimina e non scaricare di nuovo</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/unified-file-card/unified-file-card.component.html</context>
<context context-type="linenumber">37</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/unified-file-card/unified-file-card.component.html</context>
<context context-type="linenumber">40</context>
</context-group>
<note priority="1" from="description">Delete forever subscription video button</note>
</trans-unit>
<trans-unit id="9203653061903371757" datatype="html">
<source>Playlist updated successfully.</source>
<target state="translated">Playlist aggiornata con successo.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/create-playlist/create-playlist.component.ts</context>
<context context-type="linenumber">69</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/create-playlist/create-playlist.component.ts</context>
<context context-type="linenumber">75</context>
</context-group>
</trans-unit>
<trans-unit id="880407735794041263" datatype="html">
<source>Download failed.</source>
<target state="translated">Download non riuscito.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/twitch-chat/twitch-chat.component.ts</context>
<context context-type="linenumber">106</context>
</context-group>
</trans-unit>
<trans-unit id="643438049907907768" datatype="html">
<source>Failed to restore database! See logs for more info.</source>
<target state="translated">Ripristino del database non riuscito! Guarda il registro del server per più informazioni.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/restore-db-dialog/restore-db-dialog.component.ts</context>
<context context-type="linenumber">42</context>
</context-group>
</trans-unit>
<trans-unit id="2859348955905483094" datatype="html">
<source>Failed to enable sharing - server error.</source>
<target state="translated">Abilitazione della condivisione non riuscita - errore server.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/share-media-dialog/share-media-dialog.component.ts</context>
<context context-type="linenumber">74</context>
</context-group>
</trans-unit>
<trans-unit id="3561468911579213356" datatype="html">
<source>Sharing disabled.</source>
<target state="translated">Condivisione disabilitata.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/share-media-dialog/share-media-dialog.component.ts</context>
<context context-type="linenumber">79</context>
</context-group>
</trans-unit>
<trans-unit id="8692976466689769553" datatype="html">
<source>Failed to disable sharing - server error.</source>
<target state="translated">Disabilitazione della condivisione non riuscita - errore server.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/share-media-dialog/share-media-dialog.component.ts</context>
<context context-type="linenumber">85</context>
</context-group>
</trans-unit>
<trans-unit id="2876893175497409225" datatype="html">
<source>Update failed. Check logs for more details.</source>
<target state="translated">Aggiornamento non riuscito. Guarda il registro per più dettagli.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/update-progress-dialog/update-progress-dialog.component.ts</context>
<context context-type="linenumber">30</context>
</context-group>
</trans-unit>
<trans-unit id="3317b8688eb2cfabc4021cd7b2926b32f3864ad2" datatype="html">
<source>Choose a date</source>
<target state="translated">Seleziona una data</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/update-task-schedule-dialog/update-task-schedule-dialog.component.html</context>
<context context-type="linenumber">22</context>
</context-group>
<note priority="1" from="description">Choose a date</note>
</trans-unit>
<trans-unit id="7840375760456214518" datatype="html">
<source>Failed to disable sharing.</source>
<target state="translated">Disabilitazione della condivisione non riuscita.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/share-media-dialog/share-media-dialog.component.ts</context>
<context context-type="linenumber">82</context>
</context-group>
</trans-unit>
<trans-unit id="f8c7be184fefd6750e4e5d0c7a90e74721c58f8a" datatype="html">
<source>Uploader</source>
<target state="translated">Caricato da</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/video-info-dialog/video-info-dialog.component.html</context>
<context context-type="linenumber">17</context>
</context-group>
<note priority="1" from="description">Uploader</note>
</trans-unit>
<trans-unit id="d49d5d6786b69d140e20cfddfe29690a19641a88" datatype="html">
<source>Thumbnail path</source>
<target state="translated">Percorso della miniatura</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/video-info-dialog/video-info-dialog.component.html</context>
<context context-type="linenumber">26</context>
</context-group>
<note priority="1" from="description">Thumbnail path</note>
</trans-unit>
<trans-unit id="8314249599019746316" datatype="html">
<source>Download failed!</source>
<target state="translated">Download non riuscito!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/main/main.component.ts</context>
<context context-type="linenumber">387</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/main/main.component.ts</context>
<context context-type="linenumber">775</context>
</context-group>
</trans-unit>
<trans-unit id="6789263921624845085" datatype="html">
<source>Failed to load playlist!</source>
<target state="translated">Caricamento della playlist non riuscito!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/player/player.component.ts</context>
<context context-type="linenumber">186</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/player/player.component.ts</context>
<context context-type="linenumber">189</context>
</context-group>
</trans-unit>
<trans-unit id="4c9a15ab7fb3dce1002ea7aea4ecada3c1ee12e9" datatype="html">
<source>Generating an ID/secret is easy!</source>
<target state="translated">Generare un ID/secret è facile!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">267</context>
</context-group>
<note priority="1" from="description">Twitch Client ID setting hint</note>
</trans-unit>
<trans-unit id="8506540da14d205ea092b4c856e242ed7f500643" datatype="html">
<source>Twitch Client Secret</source>
<target state="translated">Secret Client Twitch</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">272</context>
</context-group>
<note priority="1" from="description">Twitch Client Secret setting placeholder</note>
</trans-unit>
<trans-unit id="4604336107574138791" datatype="html">
<source>Chrome users must drag the 'Alternate URL' link to your bookmarks.</source>
<target state="translated">Gli utenti di Chrome devono trascinare il link del 'Alternate URL' nei preferiti.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">237</context>
</context-group>
</trans-unit>
<trans-unit id="4257962986336738751" datatype="html">
<source>Successfully killed all downloads!</source>
<target state="translated">Uccisi con successo tutti i download!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">292</context>
</context-group>
</trans-unit>
<trans-unit id="1942965859829798388" datatype="html">
<source>Restarting!</source>
<target state="translated">Riavviando!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">307</context>
</context-group>
</trans-unit>
<trans-unit id="6224607866493148072" datatype="html">
<source>Failed to restart the server.</source>
<target state="translated">Riavvio del server non riuscito.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">309</context>
</context-group>
</trans-unit>
<trans-unit id="2600933489084742998" datatype="html">
<source>Failed to kill all downloads! Check logs for details.</source>
<target state="translated">Impossibile uccidere tutti i download! Guarda il registro per più informazioni.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">295</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">299</context>
</context-group>
</trans-unit>
<trans-unit id="1716030487077666916" datatype="html">
<source>Failed to transfer DB -- API call failed. See browser logs for details.</source>
<target state="translated">Trasferimento del DB non riuscito -- Errore chiamata API. Guarda il registro del browser per più informazioni.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">347</context>
</context-group>
</trans-unit>
<trans-unit id="6018050954136387828" datatype="html">
<source>Connection successful!</source>
<target state="translated">Connessione avvenuta con successo!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">357</context>
</context-group>
</trans-unit>
<trans-unit id="4021495815084152271" datatype="html">
<source>Connection failed! Error:</source>
<target state="translated">Connessione non riuscita! Errore:</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">359</context>
</context-group>
</trans-unit>
</body>
</file>
</xliff>

File diff suppressed because it is too large Load Diff