Compare commits
41 Commits
Author | SHA1 | Date |
---|---|---|
Paul van Tilburg | 49728ea6dd | |
Paul van Tilburg | 97b8a0b8bd | |
Paul van Tilburg | 36cfa2d0ff | |
Paul van Tilburg | a9e0e2417d | |
Paul van Tilburg | 263d8272da | |
Paul van Tilburg | db2d7f3f6c | |
Paul van Tilburg | 1a8f8d67fa | |
Paul van Tilburg | f4f9578c0e | |
Paul van Tilburg | 6d6895066f | |
Paul van Tilburg | f3c4c5071f | |
Paul van Tilburg | b0cb9d984a | |
Paul van Tilburg | 0c49df352d | |
Paul van Tilburg | 64ee93c553 | |
Paul van Tilburg | 613d50bf30 | |
Paul van Tilburg | fd4a26715e | |
Paul van Tilburg | 8850e16c4a | |
Paul van Tilburg | 06e0a5ecd5 | |
Paul van Tilburg | 29f3975d62 | |
Paul van Tilburg | a05106fecf | |
Paul van Tilburg | c128bfea62 | |
Paul van Tilburg | f7a5477804 | |
Paul van Tilburg | 9fc9990c27 | |
Paul van Tilburg | 05f88dbb9e | |
Paul van Tilburg | 409a69604e | |
Paul van Tilburg | b958734e92 | |
Paul van Tilburg | cb43d91b64 | |
Paul van Tilburg | f75fc513f9 | |
Paul van Tilburg | 5e9486e81a | |
Paul van Tilburg | 0ff54dbf03 | |
Paul van Tilburg | 1af19270cc | |
Paul van Tilburg | 133bc0ac27 | |
Paul van Tilburg | 51c3874820 | |
Paul van Tilburg | fd4e1b00a1 | |
Paul van Tilburg | bae34b6858 | |
Paul van Tilburg | 501bd9329c | |
Paul van Tilburg | 81979cd5e0 | |
Paul van Tilburg | 8e4045572c | |
Paul van Tilburg | 83d025c785 | |
Paul van Tilburg | 7f1120fd47 | |
Paul van Tilburg | 371b758962 | |
Paul van Tilburg | 2cd756254b |
|
@ -0,0 +1,46 @@
|
|||
name: "Check and lint using Cargo"
|
||||
|
||||
on:
|
||||
- pull_request
|
||||
- push
|
||||
- workflow_dispatch
|
||||
|
||||
jobs:
|
||||
check_lint:
|
||||
name: Check and lint
|
||||
runs-on: debian-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust stable toolchain
|
||||
uses: https://github.com/actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Run cargo check
|
||||
uses: https://github.com/actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
|
||||
- name: Run cargo clippy
|
||||
uses: https://github.com/actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
||||
|
||||
- name: Run cargo fmt
|
||||
uses: https://github.com/actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
# TODO: Add a test suite first!
|
||||
# - name: Run cargo test
|
||||
# uses: https://github.com/actions-rs/cargo@v1
|
||||
# with:
|
||||
# command: test
|
||||
# args: --all-features
|
|
@ -0,0 +1,112 @@
|
|||
name: "Release"
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: "Release"
|
||||
runs-on: debian-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Determine the version of the release
|
||||
run: |
|
||||
VERSION=${GITHUB_REF_NAME#v}
|
||||
echo "Releasing version: $VERSION"
|
||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Get the release notes from the changelog
|
||||
run: |
|
||||
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
|
||||
RELEASE_NOTES=$(sed -n -e "/^## \[$VERSION\]/,/^## \[/{//"'!'"p;}" CHANGELOG.md | sed -e '1d;$d')
|
||||
echo "Release notes:"
|
||||
echo
|
||||
echo "$RELEASE_NOTES"
|
||||
echo "RELEASE_NOTES<<$EOF" >> "$GITHUB_ENV"
|
||||
echo "$RELEASE_NOTES" >> "$GITHUB_ENV"
|
||||
echo "$EOF" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '>=1.20.1'
|
||||
|
||||
- name: Release to Gitea
|
||||
uses: actions/release-action@main
|
||||
with:
|
||||
# This is available by default.
|
||||
api_key: '${{ secrets.RELEASE_TOKEN }}'
|
||||
files: FIXME
|
||||
title: 'Release ${{ env.VERSION }}'
|
||||
body: '${{ env.RELEASE_NOTES }}'
|
||||
|
||||
release-crate:
|
||||
name: "Release crate"
|
||||
runs-on: debian-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Rust stable toolchain
|
||||
uses: https://github.com/actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
|
||||
- name: Use sparse Cargo index for crates.io
|
||||
run: echo -e '[registries.crates-io]\nprotocol = "sparse"' >> /root/.cargo/config.toml
|
||||
|
||||
- name: Register the Gitea crate registry with Cargo
|
||||
run: echo -e '[registries.luon]\nindex = "https://git.luon.net/paul/_cargo-index.git"' >> /root/.cargo/config.toml
|
||||
|
||||
- name: Run cargo publish
|
||||
uses: https://github.com/actions-rs/cargo@v1
|
||||
env:
|
||||
# This needs to be provided for the repository; no login necessary as a result.
|
||||
CARGO_REGISTRIES_LUON_TOKEN: '${{ secrets.CARGO_TOKEN }}'
|
||||
with:
|
||||
command: publish
|
||||
args: --registry luon
|
||||
|
||||
release-deb:
|
||||
name: "Release Debian package"
|
||||
runs-on: debian-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Rust stable toolchain
|
||||
uses: https://github.com/actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
|
||||
- name: Install cargo-deb
|
||||
uses: https://github.com/brndnmtthws/rust-action-cargo-binstall@v1
|
||||
with:
|
||||
packages: cargo-deb
|
||||
|
||||
- name: Run cargo-deb
|
||||
uses: https://github.com/actions-rs/cargo@v1
|
||||
with:
|
||||
command: deb
|
||||
|
||||
- name: Publish Debian package
|
||||
env:
|
||||
DEB_REPO_TOKEN: '${{ secrets.DEB_REPO_TOKEN }}'
|
||||
run: |
|
||||
curl --config <(printf "user=%s:%s" paul "${DEB_REPO_TOKEN}") \
|
||||
--upload-file target/debian/podbringer*.deb \
|
||||
https://git.luon.net/api/packages/paul/debian/pool/bookworm/main/upload
|
106
CHANGELOG.md
106
CHANGELOG.md
|
@ -7,6 +7,100 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.5.3] - 2024-02-27
|
||||
|
||||
### Changed
|
||||
|
||||
* Update dependency on `cached`
|
||||
|
||||
### Security
|
||||
|
||||
* Update dependencies, fixes security advisories:
|
||||
* [RUSTSEC-2024-0003](https://rustsec.org/advisories/RUSTSEC-2024-0003)
|
||||
* [RUSTSEC-2023-0072](https://rustsec.org/advisories/RUSTSEC-2024-0072)
|
||||
* [RUSTSEC-2023-0074](https://rustsec.org/advisories/RUSTSEC-2024-0072)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Handle paging information begin absent; fixes short feeds for Mixcloud (#17)
|
||||
|
||||
## [0.5.2] - 2023-11-03
|
||||
|
||||
### Security
|
||||
|
||||
* Update dependencies
|
||||
([RUSTSEC-2020-0071](https://rustsec.org/advisories/RUSTSEC-2020-0071.html))
|
||||
|
||||
### Changed
|
||||
|
||||
* Switch to Rocket 0.5 RC4
|
||||
* Update dependency on `cached`
|
||||
|
||||
## [0.5.1] - 2023-08-25
|
||||
|
||||
### Changed
|
||||
|
||||
* Bump the dependency on `youtube_dl`
|
||||
* Update release Gitea Actions workflow; add seperate job to release Debian
|
||||
package to the new repository
|
||||
|
||||
### Security
|
||||
|
||||
* Update dependencies
|
||||
([RUSTSEC-2023-0034](https://rustsec.org/advisories/RUSTSEC-2023-0034),
|
||||
[RUSTSEC-2023-0044](https://rustsec.org/advisories/RUSTSEC-2023-0044),
|
||||
[RUSTSEC-2023-0052](https://rustsec.org/advisories/RUSTSEC-2023-0052))
|
||||
|
||||
## [0.5.0] - 2023-06-08
|
||||
|
||||
### Added
|
||||
|
||||
* Add full release Gitea Actions workflow
|
||||
|
||||
### Changed
|
||||
|
||||
* Simplify GItea Actions check and lint workflow
|
||||
|
||||
### Fixed
|
||||
|
||||
* Differentiate between publish and update time for items
|
||||
|
||||
## [0.4.1] - 2023-04-11
|
||||
|
||||
### Changed
|
||||
|
||||
* Select only direct HTTP MP4 audio streams for the Mixcloud back-end
|
||||
|
||||
## [0.4.0] - 2023-03-24
|
||||
|
||||
### Added
|
||||
|
||||
* Add Gitea Actions workflow for cargo
|
||||
|
||||
### Changed
|
||||
|
||||
* Update dependencies on `cached` and `youtube_dl`
|
||||
* Update to `rocket` version 0.5.0-rc.3
|
||||
* Select only MP4 audio streams for the YouTube back-end (experimental)
|
||||
* Remove parameters from MIME types to prevent clients tripping over them
|
||||
|
||||
### Fixed
|
||||
|
||||
* Bump the dependency on `ytextract` (#14)
|
||||
* Fix typo in the documentation
|
||||
|
||||
### Security
|
||||
|
||||
* Update dependencies
|
||||
([RUSTSEC-2021-0145](https://rustsec.org/advisories/RUSTSEC-2021-0145.html),
|
||||
[RUSTSEC-2020-0016](https://rustsec.org/advisories/RUSTSEC-2020-0016.html),
|
||||
[RUSTSEC-2023-0001](https://rustsec.org/advisories/RUSTSEC-2023-0001.html),
|
||||
[RUSTSEC-2023-0005](https://rustsec.org/advisories/RUSTSEC-2023-0005.html),
|
||||
[RUSTSEC-2023-0018](https://rustsec.org/advisories/RUSTSEC-2023-0018.html),
|
||||
[RUSTSEC-2023-0022](https://rustsec.org/advisories/RUSTSEC-2023-0022.html),
|
||||
[RUSTSEC-2023-0023](https://rustsec.org/advisories/RUSTSEC-2023-0023.html),
|
||||
[RUSTSEC-2023-0024](https://rustsec.org/advisories/RUSTSEC-2023-0024.html))
|
||||
|
||||
## [0.3.0] - 2022-12-24
|
||||
|
||||
### Added
|
||||
|
@ -56,7 +150,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
Initial release.
|
||||
|
||||
[Unreleased]: https://git.luon.net/paul/podbringer/compare/v0.3.0...HEAD
|
||||
[0.1.0]: https://git.luon.net/paul/podbringer/commits/tag/v0.1.0
|
||||
[0.2.0]: https://git.luon.net/paul/podbringer/compare/v0.1.0..v0.2.0
|
||||
[Unreleased]: https://git.luon.net/paul/podbringer/compare/v0.5.3...HEAD
|
||||
[0.5.3]: https://git.luon.net/paul/podbringer/compare/v0.5.1..v0.5.2
|
||||
[0.5.2]: https://git.luon.net/paul/podbringer/compare/v0.5.1..v0.5.2
|
||||
[0.5.1]: https://git.luon.net/paul/podbringer/compare/v0.5.0..v0.5.1
|
||||
[0.5.0]: https://git.luon.net/paul/podbringer/compare/v0.4.1..v0.5.0
|
||||
[0.4.1]: https://git.luon.net/paul/podbringer/compare/v0.4.0..v0.4.1
|
||||
[0.4.0]: https://git.luon.net/paul/podbringer/compare/v0.3.0..v0.4.0
|
||||
[0.3.0]: https://git.luon.net/paul/podbringer/compare/v0.2.0..v0.3.0
|
||||
[0.2.0]: https://git.luon.net/paul/podbringer/compare/v0.1.0..v0.2.0
|
||||
[0.1.0]: https://git.luon.net/paul/podbringer/commits/tag/v0.1.0
|
||||
|
|
File diff suppressed because it is too large
Load Diff
11
Cargo.toml
11
Cargo.toml
|
@ -1,26 +1,27 @@
|
|||
[package]
|
||||
name = "podbringer"
|
||||
version = "0.3.0"
|
||||
version = "0.5.3"
|
||||
authors = ["Paul van Tilburg <paul@luon.net>"]
|
||||
edition = "2021"
|
||||
description = "Web service that provides podcasts for services that don't offer them (anymore)"
|
||||
readme = "README.md"
|
||||
repository = "https://git.luon.net/paul/podbringer"
|
||||
license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.57"
|
||||
cached = { version = "0.41.0", features = ["async"] }
|
||||
cached = { version = "0.49.2", features = ["async"] }
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
enum_dispatch = "0.3.8"
|
||||
mime-db = "1.6.0"
|
||||
reqwest = { version = "0.11.10", features = ["json"] }
|
||||
rocket = { version = "0.5.0-rc.2", features = ["json"] }
|
||||
rocket = { version = "0.5.0-rc.3", features = ["json"] }
|
||||
rocket_dyn_templates = { version = "0.1.0-rc.2", features = ["tera"] }
|
||||
rss = "2.0.1"
|
||||
thiserror = "1.0.31"
|
||||
url = { version = "2.2.2", features = ["serde"] }
|
||||
youtube_dl = { version = "0.7.0", features = ["tokio"] }
|
||||
ytextract = "0.11.1"
|
||||
youtube_dl = { version = "0.9.0", features = ["tokio"] }
|
||||
ytextract = "0.11.2"
|
||||
|
||||
[package.metadata.deb]
|
||||
maintainer = "Paul van Tilburg <paul@luon.net>"
|
||||
|
|
|
@ -65,7 +65,7 @@ provide the limit in the URL by setting the `limit` parameter.
|
|||
For example, to get up until 1000 items the URL becomes:
|
||||
|
||||
```text
|
||||
https://my.domain.tld/podbringer/feed/mixcloud/myfavouriteband?limit=1000`
|
||||
https://my.domain.tld/podbringer/feed/mixcloud/myfavouriteband?limit=1000
|
||||
```
|
||||
|
||||
### Service: Mixcloud
|
||||
|
|
|
@ -109,6 +109,9 @@ pub(crate) struct Item {
|
|||
/// The URL of the image of the item.
|
||||
pub(crate) image: Option<Url>,
|
||||
|
||||
/// The timestamp the item was published.
|
||||
pub(crate) published_at: DateTime<Utc>,
|
||||
|
||||
/// The timestamp the item was last updated.
|
||||
pub(crate) updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ const FILES_BASE_URL: &str = "https://www.mixcloud.com";
|
|||
const DEFAULT_BITRATE: u64 = 64 * 1024;
|
||||
|
||||
/// The default file (MIME) type used by Mixcloud.
|
||||
const DEFAULT_FILE_TYPE: &str = "audio/mpeg";
|
||||
const DEFAULT_FILE_TYPE: &str = "audio/mp4";
|
||||
|
||||
/// The default page size.
|
||||
const DEFAULT_PAGE_SIZE: usize = 50;
|
||||
|
@ -66,11 +66,16 @@ impl super::Backend for Backend {
|
|||
let count = cloudcasts_res.items.len();
|
||||
cloudcasts.extend(cloudcasts_res.items);
|
||||
|
||||
// Check if any paging information is present.
|
||||
let Some(paging) = cloudcasts_res.paging else {
|
||||
break;
|
||||
};
|
||||
|
||||
// Continue onto the next URL in the paging, if there is one and the limit was not
|
||||
// reached.
|
||||
limit = limit.saturating_sub(count);
|
||||
offset += count;
|
||||
match (limit, cloudcasts_res.paging.next) {
|
||||
match (limit, paging.next) {
|
||||
(0, Some(_)) => break,
|
||||
(_, Some(next_url)) => {
|
||||
cloudcasts_url = Url::parse(&next_url)?;
|
||||
|
@ -126,8 +131,8 @@ pub(crate) struct CloudcastsResponse {
|
|||
#[serde(rename = "data")]
|
||||
items: Vec<Cloudcast>,
|
||||
|
||||
/// The paging information.
|
||||
paging: CloudcastsPaging,
|
||||
/// The paging information (if any).
|
||||
paging: Option<CloudcastsPaging>,
|
||||
}
|
||||
|
||||
/// The Mixcloud paging info.
|
||||
|
@ -157,7 +162,10 @@ pub(crate) struct Cloudcast {
|
|||
/// The tags of the cloudcast.
|
||||
pub(crate) tags: Vec<Tag>,
|
||||
|
||||
/// The time the feed was created/started.
|
||||
/// The time the feed was created.
|
||||
pub(crate) created_time: DateTime<Utc>,
|
||||
|
||||
/// The time the feed was updated.
|
||||
pub(crate) updated_time: DateTime<Utc>,
|
||||
|
||||
/// The original URL of the cloudcast.
|
||||
|
@ -227,6 +235,7 @@ impl From<Cloudcast> for Item {
|
|||
guid: cloudcast.slug,
|
||||
keywords,
|
||||
image: Some(cloudcast.pictures.large),
|
||||
published_at: cloudcast.created_time,
|
||||
updated_at: cloudcast.updated_time,
|
||||
}
|
||||
}
|
||||
|
@ -302,7 +311,9 @@ async fn retrieve_redirect_url(download_key: &str) -> Result<String> {
|
|||
url.set_path(download_key);
|
||||
|
||||
println!("🌍 Determining direct URL for {download_key}...");
|
||||
let output = YoutubeDl::new(url).run_async().await?;
|
||||
// Select the well-supported, almost always available MP4 container format that is directly
|
||||
// available (so no HLS or DASH). This unfortunately does reduce the bitrate to 64 kbps.
|
||||
let output = YoutubeDl::new(url).format("http").run_async().await?;
|
||||
|
||||
if let YoutubeDlOutput::SingleVideo(yt_item) = output {
|
||||
yt_item.url.ok_or(Error::NoRedirectUrlFound)
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
use async_trait::async_trait;
|
||||
use cached::proc_macro::cached;
|
||||
use chrono::{DateTime, Utc};
|
||||
use chrono::{TimeZone, Utc};
|
||||
use reqwest::Url;
|
||||
use rocket::futures::StreamExt;
|
||||
use ytextract::playlist::video::{Error as YouTubeVideoError, Video as YouTubePlaylistVideo};
|
||||
|
@ -182,10 +182,13 @@ impl From<YouTubeVideoWithStream> for Item {
|
|||
) -> Self {
|
||||
let id = video.id().to_string();
|
||||
|
||||
let mime_type = stream.mime_type().to_string();
|
||||
// Ignore everything from MIME type parameter seperator on for extension look-up.
|
||||
let mime_sep = mime_type.find(';').unwrap_or(mime_type.len());
|
||||
let extension = mime_db::extension(&mime_type[..mime_sep]).unwrap_or_default();
|
||||
// Strip parameters from MIME type; some clients are scared of them and they are no
|
||||
// necessary.
|
||||
let mut mime_type = stream.mime_type().to_string();
|
||||
if let Some(sep_idx) = mime_type.find(';') {
|
||||
mime_type.truncate(sep_idx);
|
||||
}
|
||||
let extension = mime_db::extension(&mime_type).unwrap_or_default();
|
||||
let file = PathBuf::from(&id).with_extension(extension);
|
||||
let enclosure = Enclosure {
|
||||
file,
|
||||
|
@ -221,7 +224,9 @@ impl From<YouTubeVideoWithStream> for Item {
|
|||
.date()
|
||||
.and_hms_opt(12, 0, 0)
|
||||
.expect("Invalid hour, minute and/or second");
|
||||
let updated_at = DateTime::from_utc(timestamp, Utc);
|
||||
let published_at = Utc.from_utc_datetime(×tamp);
|
||||
// There is no updated at timestamp available, really.
|
||||
let updated_at = published_at;
|
||||
|
||||
Item {
|
||||
title: video.title().to_string(),
|
||||
|
@ -233,6 +238,7 @@ impl From<YouTubeVideoWithStream> for Item {
|
|||
guid: id,
|
||||
keywords,
|
||||
image,
|
||||
published_at,
|
||||
updated_at,
|
||||
}
|
||||
}
|
||||
|
@ -305,7 +311,9 @@ async fn fetch_stream(
|
|||
.streams()
|
||||
.await
|
||||
.ok()?
|
||||
.filter(|v| v.is_audio())
|
||||
// Select the well-supported, almost always available MP4 container format with
|
||||
// only an audio stream and then the one with the highest bitrate.
|
||||
.filter(|v| v.is_audio() && v.mime_type().contains("mp4"))
|
||||
.max_by_key(|v| v.bitrate())?;
|
||||
let content_length = stream.content_length().await.ok()?;
|
||||
|
||||
|
@ -334,7 +342,9 @@ async fn retrieve_redirect_url(client: &Client, video_id: &str) -> Result<String
|
|||
let stream = video
|
||||
.streams()
|
||||
.await?
|
||||
.filter(|v| v.is_audio())
|
||||
// Select the well-supported, almost always available MP4 container format with only an
|
||||
// audio stream and then the one with the highest bitrate.
|
||||
.filter(|v| v.is_audio() && v.mime_type().contains("mp4"))
|
||||
.max_by_key(|v| v.bitrate())
|
||||
.ok_or(Error::NoRedirectUrlFound)?;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
use chrono::{DateTime, NaiveDateTime, TimeZone, Utc};
|
||||
use rocket::http::uri::Absolute;
|
||||
use rocket::uri;
|
||||
use rss::extension::itunes::{
|
||||
|
@ -30,7 +30,7 @@ pub(crate) fn construct(backend_id: &str, config: &Config, channel: Channel) ->
|
|||
.build();
|
||||
let unix_timestamp = NaiveDateTime::from_timestamp_opt(0, 0)
|
||||
.expect("Out-of-range seconds or invalid nanoseconds");
|
||||
let mut last_build = DateTime::from_utc(unix_timestamp, Utc);
|
||||
let mut last_build = Utc.from_utc_datetime(&unix_timestamp);
|
||||
let generator = String::from(concat!(
|
||||
env!("CARGO_PKG_NAME"),
|
||||
" ",
|
||||
|
@ -125,7 +125,7 @@ fn construct_item(
|
|||
.categories(categories)
|
||||
.enclosure(Some(enclosure))
|
||||
.guid(Some(guid))
|
||||
.pub_date(Some(item.updated_at.to_rfc2822()))
|
||||
.pub_date(Some(item.published_at.to_rfc2822()))
|
||||
.itunes_ext(Some(itunes_ext))
|
||||
.build()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue